/* * Tigase Jabber/XMPP Server * Copyright (C) 2004-2012 "Artur Hefczyc" <artur.hefczyc@tigase.org> * * 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. * * 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. Look for COPYING file in the top folder. * If not, see http://www.gnu.org/licenses/. * * $Rev$ * Last modified by $Author$ * $Date$ */ package tigase.server.ssender; import java.io.File; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; /** * <code>FileTask</code> implements tasks for cyclic retrieving stanzas from * a directory and sending them to the StanzaHandler object. * </p> * It looks for any new stanza to send. Any single file can contain only single * stanza to send and any entry in database table can also contain only single * stanza to send. File on hard disk and record in database is deleted after * it is read. * <p> * Any file in given directory is treated the same way - Tigase assumes it * contains valid XML data with XMPP stanza to send. You can however set in * configuration, using wildchars which files contain stanzas. * All stanzas must contain complete data including correct <em>"from"</em> * and <em>"to"</em> attributes. * </p> * <p> * By default it looks for <code>*.stanza</code> files in * <code>/var/spool/jabber/</code> folder but you can specify different * directory name in initialization string. Sample initialization strings: * </p> * <pre>/var/spool/jabber/*.stanza</pre> * <pre>/var/spool/jabber/*</pre> * <p> * The last is equal to: * </p> * <pre>/var/spool/jabber/</pre> * <p> * Note the last forward slash '/' is required in such case if the last element * of the path is a directory. * </p> * <p> * <strong>Please note! Tigase must have writing permissions for this directory, * otherwise it may not function properly.</strong> * </p> * <p> * Created: Fri Apr 20 12:10:55 2007 * </p> * @author <a href="mailto:artur.hefczyc@tigase.org">Artur Hefczyc</a> * @version $Rev$ */ public class FileTask extends SenderTask { /** * Variable <code>log</code> is a class logger. */ private static final Logger log = Logger.getLogger("tigase.server.ssender.FileTask"); /** * <code>handler</code> is a reference to object processing stanza * read from database. */ private StanzaHandler handler = null; /** * <code>db_conn</code> field stores database connection string. */ private String init_str = null; /** * Variable <code>file_mask</code> keeps wildchar mask of the files which * are suposed to store XMPP stanzas to send in given directory. */ private String file_mask = null; /** * Variable <code>directory</code> keeps directory path where files * with XMPP stanzas are stored for sending. */ private String directory = null; /** * <code>init</code> method is a task specific initialization rountine. * * @param handler a <code>StanzaHandler</code> value is a reference to object * which handles all stanza retrieved from data source. The handler is * responsible for delivering stanza to destination address. * @param initString a <code>String</code> value is an initialization string * for this task. For example database tasks would expect database connection * string here, filesystem task would expect directory here. * @exception IOException if an error occurs during task or data storage * initialization. */ public void init(StanzaHandler handler, String initString) throws IOException { this.handler = handler; init_str = initString; if (init_str.endsWith(File.separator)) { file_mask = ""; directory = init_str; } else { int idx = init_str.lastIndexOf(File.separator); directory = init_str.substring(0, idx); file_mask = init_str.substring(idx+1, init_str.length()); } log.config("file_mask='" + file_mask + "', directory='" + directory + "'"); } /** * <code>getInitString</code> method returns initialization string passed * to it in <code>init()</code> method. * * @return a <code>String</code> value of initialization string. */ public String getInitString() { return init_str; } private String readFile(File ffile) throws IOException { StringBuilder result = new StringBuilder(); char[] buff = new char[16*1024]; FileReader fr = new FileReader(ffile); int res = fr.read(buff); while (res > 0) { result.append(buff, 0, res); res = fr.read(buff); } fr.close(); return result.toString(); } /** * <code>run</code> method is where all task work is done. */ public void run() { try { File fdir = new File(directory); String[] files = fdir.list(new MaskFilter(file_mask)); if (files != null) { for (String file: files) { if (log.isLoggable(Level.FINEST)) { log.finest("Processing file: " + file); } File ffile = new File(fdir, file); String stanza = readFile(ffile); handler.handleStanza(stanza); ffile.delete(); } } } catch (IOException e) { // Let's ignore it for now. log.log(Level.WARNING, "Error retrieving stanzas from database: ", e); // It should probably do kind of auto-stop??? // if so just uncomment below line: //this.cancel(); } } private static class MaskFilter implements FilenameFilter { private String mask = null; private MaskFilter(String mask) { if (mask.startsWith("*")) { this.mask = mask.substring(1); } else { this.mask = mask; } } public boolean accept(final File file, final String name) { return name.endsWith(mask); } } }