Complete set of classes for cron4j.

This commit is contained in:
MobiusDev
2017-08-08 12:19:36 +00:00
parent 151665d044
commit 09dafa9dce
78 changed files with 12240 additions and 210 deletions

View File

@ -1,18 +1,20 @@
/*
* This file is part of the L2J Mobius project.
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;

View File

@ -0,0 +1,606 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
/**
* <p>
* A parser for crontab-like formatted files and streams.
* </p>
* <p>
* If you want to schedule a list of tasks declared in a crontab-like file you don't need the CronParser, since you can do it by adding the file to the scheduler, with the {@link Scheduler#scheduleFile(File)} method.
* </p>
* <p>
* Consider to use the CronParser if the {@link Scheduler#scheduleFile(File)} method is not enough for you. In example, you may need to fetch the task list from a remote source which is not representable as a {@link File} object (a document on a remote server, a DBMS result set and so on). To solve
* the problem you can implement your own {@link TaskCollector}, getting the advantage of the CronParser to parse easily any crontab-like content.
* </p>
* <p>
* You can parse a whole file/stream, but you can also parse a single line.
* </p>
* <p>
* A line can be empty, can contain a comment or it can be a scheduling line.
* </p>
* <p>
* A line containing no characters or a line with only space characters is considered an empty line.
* </p>
* <p>
* A line whose first non-space character is a number sign (#) is considered a comment.
* </p>
* <p>
* Empty lines and comment lines are ignored by the parser.
* </p>
* <p>
* Any other kind of line is parsed as a scheduling line.
* </p>
* <p>
* A valid scheduling line respects the following structure:
* </p>
*
* <pre>
* scheduling-pattern [options] command [args]
* </pre>
* <ul>
* <li><em>scheduling-pattern</em> is a valid scheduling pattern, according with the definition given by the {@link SchedulingPattern} class.</li>
* <li><em>options</em> is a list of optional informations used by cron4j to prepare the task execution environment. See below for a more detailed description.</li>
* <li><em>command</em> is a system valid command, such an executable call.</li>
* <li><em>args</em> is a list of optional arguments for the command.</li>
* </ul>
* <p>
* After the scheduling pattern item, other tokens in each line are space separated or delimited with double quotation marks (&quot;).
* </p>
* <p>
* Double quotation marks delimited items can take advantage of the following escape sequences:
* </p>
* <ul>
* <li>\&quot; - quotation mark</li>
* <li>\\ - back slash</li>
* <li>\/ - slash</li>
* <li>\b - back space</li>
* <li>\f - form feed</li>
* <li>\n - new line</li>
* <li>\r - carriage return</li>
* <li>\t - horizontal tab</li>
* <li>\u005c\u0075<em>four-hex-digits</em> - the character at the given unicode index</li>
* </ul>
* <p>
* The <em>options</em> token collection can include one or more of the following elements:
* </p>
* <ul>
* <li>IN:<em>file-path</em> - Redirects the command standard input channel to the specified file.</li>
* <li>OUT:<em>file-path</em> - Redirects the command standard output channel to the specified file.</li>
* <li>ERR:<em>file-path</em> - Redirects the command standard error channel to the specified file.</li>
* <li>ENV:<em>name</em>=<em>value</em> - Defines an environment variable in the scope of the command.</li>
* <li>DIR:<em>directory-path</em> - Sets the path of the working directory for the command. This feature is not supported if the executing JVM is less than 1.3.</li>
* </ul>
* <p>
* It is also possible to schedule the invocation of a method of a Java class in the scope of the parser ClassLoader. The method has to be static and it must accept an array of strings as its sole argument. To invoke a method of this kind the syntax is:
* </p>
*
* <pre>
* scheduling-pattern java:className#methodName [args]
* </pre>
* <p>
* The <em>#methodName</em> part can be omitted: in this case the <em>main(String[])</em> method will be assumed.
* </p>
* <p>
* Please note that static methods are invoked within the scheduler same JVM, without spawning any external process. Thus IN, OUT, ERR, ENV and DIR options can't be applied.
* </p>
* <p>
* Invalid scheduling lines are discarded without blocking the parsing procedure, but an error message is printed in the application standard error channel.
* </p>
* <p>
* Valid examples:
* </p>
*
* <pre>
* 0 5 * * * sol.exe
* 0,30 * * * * OUT:C:\ping.txt ping 10.9.43.55
* 0,30 4 * * * &quot;OUT:C:\Documents and Settings\Carlo\ping.txt&quot; ping 10.9.43.55
* 0 3 * * * ENV:JAVA_HOME=C:\jdks\1.4.2_15 DIR:C:\myproject OUT:C:\myproject\build.log C:\myproject\build.bat &quot;Nightly Build&quot;
* 0 4 * * * java:mypackage.MyClass#startApplication myOption1 myOption2
* </pre>
*
* @author Carlo Pelliccia
* @since 2.0
*/
public class CronParser
{
/**
* Instantiation prohibited.
*/
private CronParser()
{
}
/**
* <p>
* Builds a task list reading it from a file.
* </p>
* <p>
* The file is treated as UTF-8. If your source file is not UTF-8 encoded establish by yourself a {@link Reader} using the right charset and pass it to the {@link CronParser#parse(Reader)} method.
* </p>
* <p>
* Syntax and semantics errors in the source file are not blocking. Invalid lines are discarded, and they cause just a stack trace to be printed in the standard error channel as a notification.
* </p>
* @param file The file.
* @return The task table parsed from the file.
* @throws IOException I/O error.
*/
public static TaskTable parse(File file) throws IOException
{
InputStream stream = null;
try
{
stream = new FileInputStream(file);
return parse(stream);
}
finally
{
if (stream != null)
{
try
{
stream.close();
}
catch (Throwable t)
{
}
}
}
}
/**
* <p>
* Builds a task list reading it from an URL.
* </p>
* <p>
* Contents fetched from the URL are treated as UTF-8. If your source is not UTF-8 encoded establish by yourself a {@link Reader} using the right charset and pass it to the {@link CronParser#parse(Reader)} method.
* </p>
* <p>
* Syntax and semantics errors in the retrieved document are not blocking. Invalid lines are discarded, and they cause just a stack trace to be printed in the standard error channel as a notification.
* </p>
* @param url The URL.
* @return The task table parsed from the contents fetched from the given URL.
* @throws IOException I/O error.
*/
public static TaskTable parse(URL url) throws IOException
{
InputStream stream = null;
try
{
stream = url.openStream();
return parse(stream);
}
finally
{
if (stream != null)
{
try
{
stream.close();
}
catch (Throwable t)
{
}
}
}
}
/**
* <p>
* Builds a task list reading it from an input stream.
* </p>
* <p>
* The stream is treated as UTF-8. If your source is not UTF-8 encoded establish by yourself a {@link Reader} using the right charset and pass it to the {@link CronParser#parse(Reader)} method.
* </p>
* <p>
* Syntax and semantics errors in the source stream are not blocking. Invalid lines are discarded, and they cause just a stack trace to be printed in the standard error channel as a notification.
* </p>
* @param stream The input stream.
* @return The task table parsed from the stream contents.
* @throws IOException I/O error.
*/
public static TaskTable parse(InputStream stream) throws IOException
{
return parse(new InputStreamReader(stream, "UTF-8"));
}
/**
* <p>
* Builds a task list reading it from a reader.
* </p>
* <p>
* Syntax and semantics errors in the source reader are not blocking. Invalid lines are discarded, and they cause just a stack trace to be printed in the standard error channel as a notification.
* </p>
* @param reader The reader.
* @return The task table parsed from the contents in the reader.
* @throws IOException I/O error.
*/
public static TaskTable parse(Reader reader) throws IOException
{
TaskTable table = new TaskTable();
BufferedReader bufferedReader = new BufferedReader(reader);
try
{
String line;
while ((line = bufferedReader.readLine()) != null)
{
try
{
parseLine(table, line);
}
catch (Exception e)
{
e.printStackTrace();
continue;
}
}
}
finally
{
reader.close();
}
return table;
}
/**
* Parses a crontab-like line.
* @param table The table on which the parsed task will be stored, by side-effect.
* @param line The crontab-like line.
* @throws Exception The supplied line doesn't represent a valid task line.
*/
public static void parseLine(TaskTable table, String line) throws Exception
{
line = line.trim();
if ((line.length() == 0) || (line.charAt(0) == '#'))
{
return;
}
// Detecting the pattern.
int size = line.length();
String pattern = null;
for (int i = size; i >= 0; i--)
{
String aux = line.substring(0, i);
if (SchedulingPattern.validate(aux))
{
pattern = aux;
break;
}
}
if (pattern == null)
{
throw new Exception("Invalid cron line: " + line);
}
line = line.substring(pattern.length());
size = line.length();
// Splitting the line
ArrayList<String> splitted = new ArrayList<>();
StringBuffer current = null;
boolean quotes = false;
for (int i = 0; i < size; i++)
{
char c = line.charAt(i);
if (current == null)
{
if (c == '"')
{
current = new StringBuffer();
quotes = true;
}
else if (c > ' ')
{
current = new StringBuffer();
current.append(c);
quotes = false;
}
}
else
{
boolean closeCurrent;
if (quotes)
{
closeCurrent = (c == '"');
}
else
{
closeCurrent = (c <= ' ');
}
if (closeCurrent)
{
if (current.length() > 0)
{
String str = current.toString();
if (quotes)
{
str = escape(str);
}
splitted.add(str);
}
current = null;
}
else
{
current.append(c);
}
}
}
if ((current != null) && (current.length() > 0))
{
String str = current.toString();
if (quotes)
{
str = escape(str);
}
splitted.add(str);
current = null;
}
// Analyzing
size = splitted.size();
int status = 0;
// Status values:
// 0 -> fetching environment variables, working directory and channels
// 1 -> fetching the command and its arguments
String dirString = null;
File stdinFile = null;
File stdoutFile = null;
File stderrFile = null;
ArrayList<String> envsList = new ArrayList<>();
String command = null;
ArrayList<String> argsList = new ArrayList<>();
for (int i = 0; i < size; i++)
{
String tk = splitted.get(i);
// Check the local status.
if (status == 0)
{
// Environment variables, working directory and channels
if (tk.startsWith("ENV:"))
{
envsList.add(tk.substring(4));
continue;
}
else if (tk.startsWith("DIR:"))
{
dirString = tk.substring(4);
continue;
}
else if (tk.startsWith("IN:"))
{
stdinFile = new File(tk.substring(3));
continue;
}
else if (tk.startsWith("OUT:"))
{
stdoutFile = new File(tk.substring(4));
continue;
}
else if (tk.startsWith("ERR:"))
{
stderrFile = new File(tk.substring(4));
continue;
}
else
{
status = 1;
}
}
if (status == 1)
{
// Command or argument?
if (command == null)
{
command = tk;
}
else
{
argsList.add(tk);
}
}
}
// Task preparing.
Task task;
// Command evaluation.
if (command == null)
{
// No command!
throw new Exception("Invalid cron line: " + line);
}
else if (command.startsWith("java:"))
{
// Java inner-process.
String className = command.substring(5);
if (className.length() == 0)
{
throw new Exception("Invalid Java class name on line: " + line);
}
String methodName;
int sep = className.indexOf('#');
if (sep == -1)
{
methodName = "main";
}
else
{
methodName = className.substring(sep + 1);
className = className.substring(0, sep);
if (methodName.length() == 0)
{
throw new Exception("Invalid Java method name on line: " + line);
}
}
String[] args = new String[argsList.size()];
for (int i = 0; i < argsList.size(); i++)
{
args[i] = argsList.get(i);
}
task = new StaticMethodTask(className, methodName, args);
}
else
{
// External command.
String[] cmdarray = new String[1 + argsList.size()];
cmdarray[0] = command;
for (int i = 0; i < argsList.size(); i++)
{
cmdarray[i + 1] = argsList.get(i);
}
// Environments.
String[] envs = null;
size = envsList.size();
if (size > 0)
{
envs = new String[size];
for (int i = 0; i < size; i++)
{
envs[i] = envsList.get(i);
}
}
// Working directory.
File dir = null;
if (dirString != null)
{
dir = new File(dirString);
if (!dir.exists() || !dir.isDirectory())
{
throw new Exception("Invalid cron working directory parameter at line: " + line, new FileNotFoundException(dirString + " doesn't exist or it is not a directory"));
}
}
// Builds the task.
ProcessTask process = new ProcessTask(cmdarray, envs, dir);
// Channels.
if (stdinFile != null)
{
process.setStdinFile(stdinFile);
}
if (stdoutFile != null)
{
process.setStdoutFile(stdoutFile);
}
if (stderrFile != null)
{
process.setStderrFile(stderrFile);
}
task = process;
}
// End.
table.add(new SchedulingPattern(pattern), task);
}
/**
* Escapes special chars occurrences.
* @param str The input stream.
* @return The decoded output stream.
*/
private static String escape(String str)
{
int size = str.length();
StringBuffer b = new StringBuffer();
for (int i = 0; i < size; i++)
{
int skip = 0;
char c = str.charAt(i);
if (c == '\\')
{
if (i < (size - 1))
{
char d = str.charAt(i + 1);
if (d == '"')
{
b.append('"');
skip = 2;
}
else if (d == '\\')
{
b.append('\\');
skip = 2;
}
else if (d == '/')
{
b.append('/');
skip = 2;
}
else if (d == 'b')
{
b.append('\b');
skip = 2;
}
else if (d == 'f')
{
b.append('\f');
skip = 2;
}
else if (d == 'n')
{
b.append('\n');
skip = 2;
}
else if (d == 'r')
{
b.append('\r');
skip = 2;
}
else if (d == 't')
{
b.append('\t');
skip = 2;
}
else if (d == 'u')
{
if (i < (size - 5))
{
String hex = str.substring(i + 2, i + 6);
try
{
int code = Integer.parseInt(hex, 16);
if (code >= 0)
{
b.append((char) code);
skip = 6;
}
}
catch (NumberFormatException e)
{
}
}
}
}
}
if (skip == 0)
{
b.append(c);
}
else
{
i += (skip - 1);
}
}
return b.toString();
}
}

View File

@ -1,18 +1,20 @@
/*
* This file is part of the L2J Mobius project.
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;

View File

@ -0,0 +1,104 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
/**
* <p>
* A {@link TaskCollector} implementation, reading the task list from a group of files.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
class FileTaskCollector implements TaskCollector
{
/**
* File list.
*/
private final ArrayList<File> files = new ArrayList<>();
/**
* Adds a file.
* @param file The file.
*/
public synchronized void addFile(File file)
{
files.add(file);
}
/**
* Removes a file.
* @param file The file.
*/
public synchronized void removeFile(File file)
{
files.remove(file);
}
/**
* Returns the file list.
* @return The file list.
*/
public synchronized File[] getFiles()
{
int size = files.size();
File[] ret = new File[size];
for (int i = 0; i < size; i++)
{
ret[i] = files.get(i);
}
return ret;
}
/**
* Implements {@link TaskCollector#getTasks()}.
*/
@Override
public synchronized TaskTable getTasks()
{
TaskTable ret = new TaskTable();
int size = files.size();
for (int i = 0; i < size; i++)
{
File f = files.get(i);
TaskTable aux = null;
try
{
aux = CronParser.parse(f);
}
catch (IOException e)
{
Exception e1 = new Exception("Cannot parse cron file: " + f.getAbsolutePath(), e);
e1.printStackTrace();
}
if (aux != null)
{
int auxSize = aux.size();
for (int j = 0; j < auxSize; j++)
{
ret.add(aux.getSchedulingPattern(j), aux.getTask(j));
}
}
}
return ret;
}
}

View File

@ -0,0 +1,244 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;
/**
* A GUID generator.
* @author Carlo Pelliccia
* @since 2.0
*/
class GUIDGenerator
{
/**
* The machine descriptor, which is used to identified the underlying hardware machine.
*/
private static String MACHINE_DESCRIPTOR = getMachineDescriptor();
/**
* Generates a GUID (48 chars).
* @return The generated GUID.
*/
public static String generate()
{
StringBuffer id = new StringBuffer();
encode(id, MACHINE_DESCRIPTOR);
encode(id, Runtime.getRuntime());
encode(id, Thread.currentThread());
encode(id, System.currentTimeMillis());
encode(id, getRandomInt());
return id.toString();
}
/**
* Calculates a machine id, as an integer value.
* @return The calculated machine id.
*/
private static String getMachineDescriptor()
{
StringBuffer descriptor = new StringBuffer();
descriptor.append(System.getProperty("os.name"));
descriptor.append("::");
descriptor.append(System.getProperty("os.arch"));
descriptor.append("::");
descriptor.append(System.getProperty("os.version"));
descriptor.append("::");
descriptor.append(System.getProperty("user.name"));
descriptor.append("::");
StringBuffer b = buildNetworkInterfaceDescriptor();
if (b != null)
{
descriptor.append(b);
}
else
{
// plain old InetAddress...
InetAddress addr;
try
{
addr = InetAddress.getLocalHost();
descriptor.append(addr.getHostAddress());
}
catch (UnknownHostException e)
{
}
}
return descriptor.toString();
}
/**
* Builds a descriptor fragment using the {@link NetworkInterface} class, available since Java 1.4.
* @return A descriptor fragment, or null if the method fails.
*/
private static StringBuffer buildNetworkInterfaceDescriptor()
{
Enumeration<?> e1;
try
{
e1 = NetworkInterface.getNetworkInterfaces();
}
catch (Throwable t)
{
// not available
return null;
}
StringBuffer b = new StringBuffer();
while (e1.hasMoreElements())
{
NetworkInterface ni = (NetworkInterface) e1.nextElement();
StringBuffer b1 = getMACAddressDescriptor(ni);
StringBuffer b2 = getInetAddressDescriptor(ni);
StringBuffer b3 = new StringBuffer();
if (b1 != null)
{
b3.append(b1);
}
if (b2 != null)
{
if (b3.length() > 0)
{
b3.append('=');
}
b3.append(b2);
}
if (b3.length() > 0)
{
if (b.length() > 0)
{
b.append(';');
}
b.append(b3);
}
}
return b;
}
/**
* Builds a descriptor fragment using the machine MAC address.
* @param ni NetworkInterface
* @return A descriptor fragment, or null if the method fails.
*/
private static StringBuffer getMACAddressDescriptor(NetworkInterface ni)
{
byte[] haddr;
try
{
haddr = ni.getHardwareAddress();
}
catch (Throwable t)
{
// not available.
haddr = null;
}
StringBuffer b = new StringBuffer();
if (haddr != null)
{
for (byte element : haddr)
{
if (b.length() > 0)
{
b.append("-");
}
String hex = Integer.toHexString(0xff & element);
if (hex.length() == 1)
{
b.append('0');
}
b.append(hex);
}
}
return b;
}
/**
* Builds a descriptor fragment using the machine inet address.
* @param ni NetworkInterface
* @return A descriptor fragment, or null if the method fails.
*/
private static StringBuffer getInetAddressDescriptor(NetworkInterface ni)
{
StringBuffer b = new StringBuffer();
Enumeration<?> e2 = ni.getInetAddresses();
while (e2.hasMoreElements())
{
InetAddress addr = (InetAddress) e2.nextElement();
if (b.length() > 0)
{
b.append(',');
}
b.append(addr.getHostAddress());
}
return b;
}
/**
* Returns a random integer value.
* @return A random integer value.
*/
private static int getRandomInt()
{
return (int) Math.round((Math.random() * Integer.MAX_VALUE));
}
/**
* Encodes an object and appends it to the buffer.
* @param b The buffer.
* @param obj The object.
*/
private static void encode(StringBuffer b, Object obj)
{
encode(b, obj.hashCode());
}
/**
* Encodes an integer value and appends it to the buffer.
* @param b The buffer.
* @param value The value.
*/
private static void encode(StringBuffer b, int value)
{
String hex = Integer.toHexString(value);
int hexSize = hex.length();
for (int i = 8; i > hexSize; i--)
{
b.append('0');
}
b.append(hex);
}
/**
* Encodes a long value and appends it to the buffer.
* @param b The buffer.
* @param value The value.
*/
private static void encode(StringBuffer b, long value)
{
String hex = Long.toHexString(value);
int hexSize = hex.length();
for (int i = 16; i > hexSize; i--)
{
b.append('0');
}
b.append(hex);
}
}

View File

@ -1,18 +1,20 @@
/*
* This file is part of the L2J Mobius project.
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;

View File

@ -1,18 +1,20 @@
/*
* This file is part of the L2J Mobius project.
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;

View File

@ -0,0 +1,100 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* LauncherThreads are used by {@link Scheduler} instances. A LauncherThread retrieves a list of task from a set of {@link TaskCollector}s. Then it launches, within a separate {@link TaskExecutor}, every retrieved task whose scheduling pattern matches the given reference time.
* @author Carlo Pelliccia
* @since 2.0
*/
class LauncherThread extends Thread
{
/**
* A GUID for this object.
*/
private final String guid = GUIDGenerator.generate();
/**
* The owner scheduler.
*/
private final Scheduler scheduler;
/**
* Task collectors, used to retrieve registered tasks.
*/
private final TaskCollector[] collectors;
/**
* A reference time for task launching.
*/
private final long referenceTimeInMillis;
/**
* Builds the launcher.
* @param scheduler The owner scheduler.
* @param collectors Task collectors, used to retrieve registered tasks.
* @param referenceTimeInMillis A reference time for task launching.
*/
public LauncherThread(Scheduler scheduler, TaskCollector[] collectors, long referenceTimeInMillis)
{
this.scheduler = scheduler;
this.collectors = collectors;
this.referenceTimeInMillis = referenceTimeInMillis;
// Thread name.
String name = "cron4j::scheduler[" + scheduler.getGuid() + "]::launcher[" + guid + "]";
setName(name);
}
/**
* Returns the GUID for this object.
* @return The GUID for this object.
*/
public Object getGuid()
{
return guid;
}
/**
* Overrides {@link Thread#run()}.
*/
@Override
public void run()
{
outer: for (int i = 0; i < collectors.length; i++)
{
TaskTable taskTable = collectors[i].getTasks();
int size = taskTable.size();
for (int j = 0; j < size; j++)
{
if (isInterrupted())
{
break outer;
}
SchedulingPattern pattern = taskTable.getSchedulingPattern(j);
if (pattern.match(scheduler.getTimeZone(), referenceTimeInMillis))
{
Task task = taskTable.getTask(j);
scheduler.spawnExecutor(task);
}
}
}
// Notifies completed.
scheduler.notifyLauncherCompleted(this);
}
}

View File

@ -0,0 +1,152 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.util.ArrayList;
/**
* <p>
* A {@link TaskCollector} implementation managing a task list in memory.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
class MemoryTaskCollector implements TaskCollector
{
/**
* Size.
*/
private final int size = 0;
/**
* The inner scheduling pattern list.
*/
private final ArrayList<SchedulingPattern> patterns = new ArrayList<>();
/**
* The inner task list.
*/
private final ArrayList<Task> tasks = new ArrayList<>();
/**
* IDs for task-pattern couples.
*/
private final ArrayList<String> ids = new ArrayList<>();
/**
* Counts how many task are currently collected by this collector.
* @return The size of the currently collected task list.
*/
public synchronized int size()
{
return size;
}
/**
* Adds a pattern and a task to the collector.
* @param pattern The scheduling pattern.
* @param task The task.
* @return An ID for the scheduled operation.
*/
public synchronized String add(SchedulingPattern pattern, Task task)
{
String id = GUIDGenerator.generate();
patterns.add(pattern);
tasks.add(task);
ids.add(id);
return id;
}
/**
* Updates a scheduling pattern in the collector.
* @param id The ID of the scheduled couple.
* @param pattern SchedulingPattern
*/
public synchronized void update(String id, SchedulingPattern pattern)
{
int index = ids.indexOf(id);
if (index > -1)
{
patterns.set(index, pattern);
}
}
/**
* Removes a task and its scheduling pattern from the collector.
* @param id The ID of the scheduled couple.
* @throws IndexOutOfBoundsException
*/
public synchronized void remove(String id) throws IndexOutOfBoundsException
{
int index = ids.indexOf(id);
if (index > -1)
{
tasks.remove(index);
patterns.remove(index);
ids.remove(index);
}
}
/**
* Retrieves a task from the collector.
* @param id The ID of the scheduled couple.
* @return The task with the specified assigned ID, or null if it doesn't exist.
*/
public synchronized Task getTask(String id)
{
int index = ids.indexOf(id);
if (index > -1)
{
return tasks.get(index);
}
return null;
}
/**
* Retrieves a scheduling pattern from the collector.
* @param id The ID of the scheduled couple.
* @return The scheduling pattern with the specified assigned ID, or null if it doesn't exist.
*/
public synchronized SchedulingPattern getSchedulingPattern(String id)
{
int index = ids.indexOf(id);
if (index > -1)
{
return patterns.get(index);
}
return null;
}
/**
* Implements {@link TaskCollector#getTasks()}.
*/
@Override
public synchronized TaskTable getTasks()
{
TaskTable ret = new TaskTable();
int size = tasks.size();
for (int i = 0; i < size; i++)
{
Task t = tasks.get(i);
SchedulingPattern p = patterns.get(i);
ret.add(p, t);
}
return ret;
}
}

View File

@ -1,18 +1,20 @@
/*
* This file is part of the L2J Mobius project.
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;

View File

@ -0,0 +1,420 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* <p>
* A built-in {@link Task} implementation which can be used to run an external process.
* </p>
* @author Carlo Pelliccia
* @since 2.1
*/
public class ProcessTask extends Task
{
/**
* The command to launch.
*/
private String[] command;
/**
* Environment variables for the spawned process, in the form <em>name=value</em>. If null the process will inherit the current JVM environment variables.
*/
private String[] envs;
/**
* Working directory for the spawned process. If null the process will inherit the current JVM working directory.
*/
private File directory;
/**
* Standard input file (optional).
*/
private File stdinFile = null;
/**
* Standard output file (optional).
*/
private File stdoutFile = null;
/**
* Standard error file (optional).
*/
private File stderrFile = null;
/**
* Creates the task.
* @param command The command to launch and its arguments.
* @param envs Environment variables for the spawned process, in the form <em>name=value</em>. If null the process will inherit the current JVM environment variables.
* @param directory Working directory for the spawned process. If null the process will inherit the current JVM working directory.
*/
public ProcessTask(String[] command, String[] envs, File directory)
{
this.command = command;
this.envs = envs;
this.directory = directory;
}
/**
* Creates the task.
* @param command The command to launch and its arguments.
* @param envs Environment variables for the spawned process, in the form <em>name=value</em>. If null the process will inherit the current JVM environment variables.
*/
public ProcessTask(String[] command, String[] envs)
{
this(command, envs, null);
}
/**
* Creates the task.
* @param command The command to launch and its arguments.
*/
public ProcessTask(String[] command)
{
this(command, null, null);
}
/**
* Creates the task.
* @param command The command to launch.
*/
public ProcessTask(String command)
{
this(new String[]
{
command
}, null, null);
}
/**
* Returns true.
*/
@Override
public boolean canBeStopped()
{
return true;
}
/**
* Returns the command executed by this task.
* @return The command executed by this task.
*/
public String[] getCommand()
{
return command;
}
/**
* Sets the command executed by this task.
* @param command The command executed by this task.
*/
public void setCommand(String[] command)
{
this.command = command;
}
/**
* Returns the environment variables, in the <em>name=value</em> form, used by the task to run its process. If null the process will inherit the current JVM environment variables.
* @return The environment variables, in the <em>name=value</em> form, used by the task to run its process. If null the process will inherit the current JVM environment variables.
*/
public String[] getEnvs()
{
return envs;
}
/**
* Sets the environment variables, in the <em>name=value</em> form, used by the task to run its process. If null the process will inherit the current JVM environment variables.
* @param envs The environment variables, in the <em>name=value</em> form, used by the task to run its process. If null the process will inherit the current JVM environment variables.
*/
public void setEnvs(String[] envs)
{
this.envs = envs;
}
/**
* Resturns the working directory for the spawned process. If null the process will inherit the current JVM working directory.
* @return The working directory for the spawned process. If null the process will inherit the current JVM working directory.
*/
public File getDirectory()
{
return directory;
}
/**
* Sets the working directory for the spawned process. If null the process will inherit the current JVM working directory.
* @param directory The working directory for the spawned process. If null the process will inherit the current JVM working directory.
*/
public void setDirectory(File directory)
{
this.directory = directory;
}
/**
* Returns the standard input file (optional). If supplied, the standard input channel of the spawned process will be read from the given file.
* @return The standard input file (optional).
*/
public File getStdinFile()
{
return stdinFile;
}
/**
* Sets the standard input file (optional). If supplied, the standard input channel of the spawned process will be read from the given file.
* @param stdinFile The standard input file (optional).
*/
public void setStdinFile(File stdinFile)
{
this.stdinFile = stdinFile;
}
/**
* Sets the standard output file (optional). If supplied, the standard output channel of the spawned process will be written in the given file.
* @param stdoutFile The standard output file (optional).
*/
public void setStdoutFile(File stdoutFile)
{
this.stdoutFile = stdoutFile;
}
/**
* Returns the standard output file (optional). If supplied, the standard output channel of the spawned process will be written in the given file.
* @return The standard output file (optional).
*/
public File getStdoutFile()
{
return stdoutFile;
}
/**
* Sets the standard error file (optional). If supplied, the standard error channel of the spawned process will be written in the given file.
* @param stderrFile The standard error file (optional).
*/
public void setStderrFile(File stderrFile)
{
this.stderrFile = stderrFile;
}
/**
* Returns the standard error file (optional). If supplied, the standard error channel of the spawned process will be written in the given file.
* @return The standard error file (optional).
*/
public File getStderrFile()
{
return stderrFile;
}
/**
* Implements {@link Task#execute(TaskExecutionContext)}. Runs the given command as a separate process and waits for its end.
*/
@Override
public void execute(TaskExecutionContext context) throws RuntimeException
{
Process p;
try
{
p = exec();
}
catch (IOException e)
{
throw new RuntimeException(toString() + " cannot be started", e);
}
InputStream in = buildInputStream(stdinFile);
OutputStream out = buildOutputStream(stdoutFile);
OutputStream err = buildOutputStream(stderrFile);
if (in != null)
{
StreamBridge b = new StreamBridge(in, p.getOutputStream());
b.start();
}
if (out != null)
{
StreamBridge b = new StreamBridge(p.getInputStream(), out);
b.start();
}
if (err != null)
{
StreamBridge b = new StreamBridge(p.getErrorStream(), err);
b.start();
}
int r;
try
{
r = p.waitFor();
}
catch (InterruptedException e)
{
throw new RuntimeException(toString() + " has been interrupted");
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (Throwable e)
{
}
}
if (out != null)
{
try
{
out.close();
}
catch (Throwable e)
{
}
}
if (err != null)
{
try
{
err.close();
}
catch (Throwable e)
{
}
}
p.destroy();
}
if (r != 0)
{
throw new RuntimeException(toString() + " returns with error code " + r);
}
}
/**
* Executes the command.
* @return The launched Process.
* @throws IOException If an I/O error occurs.
*/
private Process exec() throws IOException
{
Runtime rt = Runtime.getRuntime();
Process p;
try
{
// java 1.3+
p = rt.exec(command, envs, directory);
}
catch (NoSuchMethodError e)
{
// java 1.2
p = rt.exec(command, envs);
}
return p;
}
/**
* Prepares an {@link InputStream} on a file and returns it.
* @param file The file.
* @return The stream, or null if the file is not found.
*/
private InputStream buildInputStream(File file)
{
if (file != null)
{
try
{
return new FileInputStream(file);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
return null;
}
}
return null;
}
/**
* Prepares an {@link OutputStream} on a file and returns it.
* @param file The file.
* @return The stream, or null if the file is not found.
*/
private OutputStream buildOutputStream(File file)
{
if (file != null)
{
try
{
return new FileOutputStream(file);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
return null;
}
}
return null;
}
/**
* Prints in the returned string the elements contained in the given string array.
* @param arr The array.
* @return A string representing the supplied array contents.
*/
private static String listStrings(String[] arr)
{
if (arr == null)
{
return "null";
}
StringBuffer b = new StringBuffer();
b.append('[');
for (int i = 0; i < arr.length; i++)
{
if (i > 0)
{
b.append(", ");
}
b.append(arr[i]);
}
b.append(']');
return b.toString();
}
/**
* Overrides {@link Object#toString()}.
*/
@Override
public String toString()
{
StringBuffer b = new StringBuffer();
b.append("Task[");
b.append("cmd=");
b.append(ProcessTask.listStrings(command));
b.append(", env=");
b.append(ProcessTask.listStrings(envs));
b.append(", ");
b.append("dir=");
b.append(directory);
b.append("]");
return b.toString();
}
}

View File

@ -0,0 +1,76 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* <p>
* A {@link Task} implementation acting as a wrapper around a {@link Runnable} object.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
class RunnableTask extends Task
{
/**
* The wrapped runnable object.
*/
private final Runnable runnable;
/**
* Builds the task.
* @param runnable The wrapped Runnable object.
* @throws InvalidPatternException If the supplied pattern is not valid.
*/
public RunnableTask(Runnable runnable) throws InvalidPatternException
{
this.runnable = runnable;
}
/**
* Returns the wrapped Runnable object.
* @return The wrapped Runnable object.
*/
public Runnable getRunnable()
{
return runnable;
}
/**
* Implements {@link Task#execute(TaskExecutionContext)}, launching the {@link Runnable#run()} method on the wrapped object.
*/
@Override
public void execute(TaskExecutionContext context)
{
runnable.run();
}
/**
* Overrides {@link Object#toString()}.
*/
@Override
public String toString()
{
StringBuffer b = new StringBuffer();
b.append("Task[");
b.append("runnable=");
b.append(runnable);
b.append("]");
return b.toString();
}
}

View File

@ -0,0 +1,728 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.io.File;
import java.util.ArrayList;
import java.util.TimeZone;
/**
* <p>
* The cron4j scheduler.
* </p>
* @author Carlo Pelliccia
*/
public class Scheduler
{
/**
* A GUID for this scheduler.
*/
private final String guid = GUIDGenerator.generate();
/**
* The time zone applied by the scheduler.
*/
private TimeZone timezone = null;
/**
* The daemon flag. If true the scheduler and its spawned threads acts like daemons.
*/
private boolean daemon = false;
/**
* The state flag. If true the scheduler is started and running, otherwise it is paused and no task is launched.
*/
private boolean started = false;
/**
* Registered {@link TaskCollector}s list.
*/
private final ArrayList<TaskCollector> collectors = new ArrayList<>();
/**
* The {@link MemoryTaskCollector} used for memory stored tasks. Represented here for convenience, it is also the first element in the {@link Scheduler#collectors} list.
*/
private final MemoryTaskCollector memoryTaskCollector = new MemoryTaskCollector();
/**
* The {@link FileTaskCollector} used for reading tasks from files. Represented here for convenience, it is also the second element in the {@link Scheduler#collectors} list.
*/
private final FileTaskCollector fileTaskCollector = new FileTaskCollector();
/**
* Registered {@link SchedulerListener}s list.
*/
private final ArrayList<SchedulerListener> listeners = new ArrayList<>();
/**
* The thread checking the clock and requesting the spawning of launcher threads.
*/
private TimerThread timer = null;
/**
* Currently running {@link LauncherThread} instances.
*/
private ArrayList<LauncherThread> launchers = null;
/**
* Currently running {@link TaskExecutor} instances.
*/
private ArrayList<TaskExecutor> executors = null;
/**
* Internal lock, used to synchronize status-aware operations.
*/
private final Object lock = new Object();
/**
* It builds and prepares a brand new Scheduler instance.
*/
public Scheduler()
{
collectors.add(memoryTaskCollector);
collectors.add(fileTaskCollector);
}
/**
* It returns the GUID for this scheduler.
* @return The GUID for this scheduler.
*/
public Object getGuid()
{
return guid;
}
/**
* <p>
* Sets the time zone applied by the scheduler.
* </p>
* <p>
* Current system time is adapted to the supplied time zone before comparing it with registered scheduling patterns. The result is that any supplied scheduling pattern is treated according to the specified time zone. In example, suppose:
* </p>
* <ul>
* <li>System time: 10:00</li>
* <li>System time zone: GMT+1</li>
* <li>Scheduler time zone: GMT+3</li>
* </ul>
* <p>
* The scheduler, before comparing system time with patterns, translates 10:00 from GMT+1 to GMT+3. It means that 10:00 becomes 12:00. The resulted time is then used by the scheduler to activate tasks. So, in the given configuration at the given moment, any task scheduled as <em>0 12 * * *</em>
* will be executed, while any <em>0 10 * * *</em> will not.
* </p>
* @param timezone The time zone applied by the scheduler.
*/
public void setTimeZone(TimeZone timezone)
{
this.timezone = timezone;
}
/**
* Returns the time zone applied by the scheduler.
* @return The time zone applied by the scheduler.
*/
public TimeZone getTimeZone()
{
return timezone != null ? timezone : TimeZone.getDefault();
}
/**
* Tests whether this scheduler is a daemon scheduler.
* @return true if this scheduler is a daemon scheduler; false otherwise.
*/
public boolean isDaemon()
{
return daemon;
}
/**
* Marks this scheduler daemon flag. When a scheduler is marked as a daemon scheduler it spawns only daemon threads. The Java Virtual Machine exits when the only threads running are all daemon threads. This method must be called before the scheduler is started.
* @param on If true, the scheduler will spawn only daemon threads.
* @throws IllegalStateException If the scheduler is started.
*/
public void setDaemon(boolean on) throws IllegalStateException
{
synchronized (lock)
{
if (started)
{
throw new IllegalStateException("Scheduler already started");
}
this.daemon = on;
}
}
/**
* Tests if this scheduler is started.
* @return true if the scheduler is started, false if it is stopped.
*/
public boolean isStarted()
{
synchronized (lock)
{
return started;
}
}
/**
* Adds a {@link File} instance to the scheduler. Every minute the file will be parsed. The scheduler will execute any declared task whose scheduling pattern matches the current system time. See {@link CronParser} documentation for informations about the file contents syntax.
* @param file The {@link File} instance.
*/
public void scheduleFile(File file)
{
fileTaskCollector.addFile(file);
}
/**
* Removes a {@link File} instance previously scheduled with the {@link Scheduler#scheduleFile(File)} method.
* @param file The {@link File} instance.
*/
public void descheduleFile(File file)
{
fileTaskCollector.removeFile(file);
}
/**
* Returns an array containing any {@link File} previously scheduled with the {@link Scheduler#scheduleFile(File)} method.
* @return An array containing any {@link File} previously scheduled with the {@link Scheduler#scheduleFile(File)} method.
*/
public File[] getScheduledFiles()
{
return fileTaskCollector.getFiles();
}
/**
* Adds a custom {@link TaskCollector} instance to the scheduler. The supplied object, once added to the scheduler, will be query every minute for its task list. The scheduler will execute any of the returned tasks whose scheduling pattern matches the current system time.
* @param collector The custom {@link TaskCollector} instance.
*/
public void addTaskCollector(TaskCollector collector)
{
synchronized (collectors)
{
collectors.add(collector);
}
}
/**
* Removes a previously registered custom {@link TaskCollector} instance.
* @param collector The custom {@link TaskCollector} instance.
*/
public void removeTaskCollector(TaskCollector collector)
{
synchronized (collectors)
{
collectors.remove(collector);
}
}
/**
* Returns an array containing any custom {@link TaskCollector} instance previously registered in the scheduler with the {@link Scheduler#addTaskCollector(TaskCollector)} method.
* @return An array containing any custom {@link TaskCollector} instance previously registered in the scheduler with the {@link Scheduler#addTaskCollector(TaskCollector)} method.
*/
public TaskCollector[] getTaskCollectors()
{
synchronized (collectors)
{
// Discard the first 2 elements in the list.
int size = collectors.size() - 2;
TaskCollector[] ret = new TaskCollector[size];
for (int i = 0; i < size; i++)
{
ret[i] = collectors.get(i + 2);
}
return ret;
}
}
/**
* Adds a {@link SchedulerListener} to the scheduler. A {@link SchedulerListener} is notified every time a task is launching, has succeeded or has failed.
* @param listener The listener.
*/
public void addSchedulerListener(SchedulerListener listener)
{
synchronized (listeners)
{
listeners.add(listener);
}
}
/**
* Removes a {@link SchedulerListener} previously registered with the {@link Scheduler#addSchedulerListener(SchedulerListener)} method.
* @param listener The listener.
*/
public void removeSchedulerListener(SchedulerListener listener)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
/**
* Returns an array containing any {@link SchedulerListener} previously registered with the {@link Scheduler#addSchedulerListener(SchedulerListener)} method.
* @return An array containing any {@link SchedulerListener} previously registered with the {@link Scheduler#addSchedulerListener(SchedulerListener)} method.
*/
public SchedulerListener[] getSchedulerListeners()
{
synchronized (listeners)
{
int size = listeners.size();
SchedulerListener[] ret = new SchedulerListener[size];
for (int i = 0; i < size; i++)
{
ret[i] = listeners.get(i);
}
return ret;
}
}
/**
* Returns an array containing any currently executing task, in the form of {@link TaskExecutor} objects. Each running task is executed by a different thread. A {@link TaskExecutor} object allows the control of the running task. The inner {@link Task} representation could be retrieved, the
* status of the task could be detected and the thread could be interrupted using any standard {@link Thread} method ( {@link Thread#interrupt()}, {@link Thread#isAlive() etc}.
* @return An array containing any currently executing task, in the form of {@link TaskExecutor} objects.
*/
public TaskExecutor[] getExecutingTasks()
{
synchronized (executors)
{
int size = executors.size();
TaskExecutor[] ret = new TaskExecutor[size];
for (int i = 0; i < size; i++)
{
ret[i] = executors.get(i);
}
return ret;
}
}
/**
* This method schedules a task execution.
* @param schedulingPattern The scheduling pattern for the task.
* @param task The task, as a plain Runnable object.
* @return The task auto-generated ID assigned by the scheduler. This ID can be used later to reschedule and deschedule the task, and also to retrieve informations about it.
* @throws InvalidPatternException If the supplied pattern is not valid.
*/
public String schedule(String schedulingPattern, Runnable task) throws InvalidPatternException
{
return schedule(schedulingPattern, new RunnableTask(task));
}
/**
* This method schedules a task execution.
* @param schedulingPattern The scheduling pattern for the task.
* @param task The task, as a plain Runnable object.
* @return The task auto-generated ID assigned by the scheduler. This ID can be used later to reschedule and deschedule the task, and also to retrieve informations about it.
* @throws InvalidPatternException If the supplied pattern is not valid.
* @since 2.0
*/
public String schedule(String schedulingPattern, Task task) throws InvalidPatternException
{
return schedule(new SchedulingPattern(schedulingPattern), task);
}
/**
* This method schedules a task execution.
* @param schedulingPattern The scheduling pattern for the task.
* @param task The task, as a plain Runnable object.
* @return The task auto-generated ID assigned by the scheduler. This ID can be used later to reschedule and deschedule the task, and also to retrieve informations about it.
* @since 2.0
*/
public String schedule(SchedulingPattern schedulingPattern, Task task)
{
return memoryTaskCollector.add(schedulingPattern, task);
}
/**
* This method changes the scheduling pattern of a task.
* @param id The ID assigned to the previously scheduled task.
* @param schedulingPattern The new scheduling pattern for the task.
* @throws InvalidPatternException If the supplied pattern is not valid.
* @deprecated Use {@link Scheduler#reschedule(String, String)}.
*/
@Deprecated
public void reschedule(Object id, String schedulingPattern) throws InvalidPatternException
{
reschedule((String) id, new SchedulingPattern(schedulingPattern));
}
/**
* This method changes the scheduling pattern of a task.
* @param id The ID assigned to the previously scheduled task.
* @param schedulingPattern The new scheduling pattern for the task.
* @throws InvalidPatternException If the supplied pattern is not valid.
*/
public void reschedule(String id, String schedulingPattern) throws InvalidPatternException
{
reschedule(id, new SchedulingPattern(schedulingPattern));
}
/**
* This method changes the scheduling pattern of a task.
* @param id The ID assigned to the previously scheduled task.
* @param schedulingPattern The new scheduling pattern for the task.
* @since 2.0
*/
public void reschedule(String id, SchedulingPattern schedulingPattern)
{
memoryTaskCollector.update(id, schedulingPattern);
}
/**
* This methods cancels the scheduling of a task.
* @param id The ID of the task.
* @deprecated Use {@link Scheduler#deschedule(String)}.
*/
@Deprecated
public void deschedule(Object id)
{
deschedule((String) id);
}
/**
* This methods cancels the scheduling of a task.
* @param id The ID of the task.
*/
public void deschedule(String id)
{
memoryTaskCollector.remove(id);
}
/**
* This method retrieves a previously scheduled task.
* @param id The task ID.
* @return The requested task, or null if the task was not found.
* @since 2.0
*/
public Task getTask(String id)
{
return memoryTaskCollector.getTask(id);
}
/**
* This method retrieves a previously scheduled task scheduling pattern.
* @param id The task ID.
* @return The requested scheduling pattern, or null if the task was not found.
* @since 2.0
*/
public SchedulingPattern getSchedulingPattern(String id)
{
return memoryTaskCollector.getSchedulingPattern(id);
}
/**
* This method retrieves the Runnable object of a previously scheduled task.
* @param id The task ID.
* @return The Runnable object of the task, or null if the task was not found.
* @deprecated Use {@link Scheduler#getTask(String)}.
*/
@Deprecated
public Runnable getTaskRunnable(Object id)
{
Task task = getTask((String) id);
if (task instanceof RunnableTask)
{
RunnableTask rt = (RunnableTask) task;
return rt.getRunnable();
}
return null;
}
/**
* This method retrieves the scheduling pattern of a previously scheduled task.
* @param id The task ID.
* @return The scheduling pattern of the task, or null if the task was not found.
* @deprecated Use {@link Scheduler#getSchedulingPattern(String)}.
*/
@Deprecated
public String getTaskSchedulingPattern(Object id)
{
return getSchedulingPattern((String) id).toString();
}
/**
* Executes immediately a task, without scheduling it.
* @param task The task.
* @return The {@link TaskExecutor} executing the given task.
* @throws IllegalStateException If the scheduler is not started.
*/
public TaskExecutor launch(Task task)
{
synchronized (lock)
{
if (!started)
{
throw new IllegalStateException("Scheduler not started");
}
return spawnExecutor(task);
}
}
/**
* This method starts the scheduler. When the scheduled is started the supplied tasks are executed at the given moment.
* @throws IllegalStateException Thrown if this scheduler is already started.
*/
public void start() throws IllegalStateException
{
synchronized (lock)
{
if (started)
{
throw new IllegalStateException("Scheduler already started");
}
// Initializes required lists.
launchers = new ArrayList<>();
executors = new ArrayList<>();
// Starts the timer thread.
timer = new TimerThread(this);
timer.setDaemon(daemon);
timer.start();
// Change the state of the scheduler.
started = true;
}
}
/**
* This method stops the scheduler execution. Before returning, it waits the end of all the running tasks previously launched. Once the scheduler has been stopped it can be started again with a start() call.
* @throws IllegalStateException Thrown if this scheduler is not started.
*/
public void stop() throws IllegalStateException
{
synchronized (lock)
{
if (!started)
{
throw new IllegalStateException("Scheduler not started");
}
// Interrupts the timer and waits for its death.
timer.interrupt();
tillThreadDies(timer);
timer = null;
// Interrupts any running launcher and waits for its death.
for (;;)
{
LauncherThread launcher = null;
synchronized (launchers)
{
if (launchers.size() == 0)
{
break;
}
launcher = launchers.remove(0);
}
launcher.interrupt();
tillThreadDies(launcher);
}
launchers = null;
// Interrupts any running executor and waits for its death.
// Before exiting wait for all the active tasks end.
for (;;)
{
TaskExecutor executor = null;
synchronized (executors)
{
if (executors.size() == 0)
{
break;
}
executor = executors.remove(0);
}
if (executor.canBeStopped())
{
executor.stop();
}
tillExecutorDies(executor);
}
executors = null;
// Change the state of the object.
started = false;
}
}
// -- PACKAGE RESERVED METHODS --------------------------------------------
/**
* Starts a launcher thread.
* @param referenceTimeInMillis Reference time in millis for the launcher.
* @return The spawned launcher.
*/
LauncherThread spawnLauncher(long referenceTimeInMillis)
{
TaskCollector[] nowCollectors;
synchronized (collectors)
{
int size = collectors.size();
nowCollectors = new TaskCollector[size];
for (int i = 0; i < size; i++)
{
nowCollectors[i] = collectors.get(i);
}
}
LauncherThread l = new LauncherThread(this, nowCollectors, referenceTimeInMillis);
synchronized (launchers)
{
launchers.add(l);
}
l.setDaemon(daemon);
l.start();
return l;
}
/**
* Starts the given task within a task executor.
* @param task The task.
* @return The spawned task executor.
*/
TaskExecutor spawnExecutor(Task task)
{
TaskExecutor e = new TaskExecutor(this, task);
synchronized (executors)
{
executors.add(e);
}
e.start(daemon);
return e;
}
/**
* This method is called by a launcher thread to notify that the execution is completed.
* @param launcher The launcher which has completed its task.
*/
void notifyLauncherCompleted(LauncherThread launcher)
{
synchronized (launchers)
{
launchers.remove(launcher);
}
}
/**
* This method is called by a task executor to notify that the execution is completed.
* @param executor The executor which has completed its task.
*/
void notifyExecutorCompleted(TaskExecutor executor)
{
synchronized (executors)
{
executors.remove(executor);
}
}
/**
* Notifies every registered listener that a task is going to be launched.
* @param executor The task executor.
*/
void notifyTaskLaunching(TaskExecutor executor)
{
synchronized (listeners)
{
int size = listeners.size();
for (int i = 0; i < size; i++)
{
SchedulerListener l = listeners.get(i);
l.taskLaunching(executor);
}
}
}
/**
* Notifies every registered listener that a task execution has successfully completed.
* @param executor The task executor.
*/
void notifyTaskSucceeded(TaskExecutor executor)
{
synchronized (listeners)
{
int size = listeners.size();
for (int i = 0; i < size; i++)
{
SchedulerListener l = listeners.get(i);
l.taskSucceeded(executor);
}
}
}
/**
* Notifies every registered listener that a task execution has failed due to an uncaught exception.
* @param executor The task executor.
* @param exception The exception.
*/
void notifyTaskFailed(TaskExecutor executor, Throwable exception)
{
synchronized (listeners)
{
int size = listeners.size();
if (size > 0)
{
for (int i = 0; i < size; i++)
{
SchedulerListener l = listeners.get(i);
l.taskFailed(executor, exception);
}
}
else
{
// Logs on console if no one has been notified about it.
exception.printStackTrace();
}
}
}
// -- PRIVATE METHODS -----------------------------------------------------
/**
* It waits until the given thread is dead. It is similar to {@link Thread#join()}, but this one avoids {@link InterruptedException} instances.
* @param thread The thread.
*/
private void tillThreadDies(Thread thread)
{
boolean dead = false;
do
{
try
{
thread.join();
dead = true;
}
catch (InterruptedException e)
{
}
}
while (!dead);
}
/**
* It waits until the given task executor is dead. It is similar to {@link TaskExecutor#join()}, but this one avoids {@link InterruptedException} instances.
* @param executor The task executor.
*/
private void tillExecutorDies(TaskExecutor executor)
{
boolean dead = false;
do
{
try
{
executor.join();
dead = true;
}
catch (InterruptedException e)
{
}
}
while (!dead);
}
}

View File

@ -0,0 +1,48 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* <p>
* Implement this interface and register your instance with the {@link Scheduler#addSchedulerListener(SchedulerListener)} method to receive notifications about scheduled task executions.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
public interface SchedulerListener
{
/**
* This one is called by the scheduler when a task execution is starting.
* @param executor The task executor.
*/
public void taskLaunching(TaskExecutor executor);
/**
* This one is called by the scheduler to notify that a task execution has been successfully completed.
* @param executor The task executor.
*/
public void taskSucceeded(TaskExecutor executor);
/**
* This one is called by the scheduler to notify that a task execution has failed.
* @param executor The task executor.
* @param exception The exception representing the failure notification.
*/
public void taskFailed(TaskExecutor executor, Throwable exception);
}

View File

@ -1,18 +1,20 @@
/*
* This file is part of the L2J Mobius project.
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;

View File

@ -0,0 +1,52 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* <p>
* A scheduling patterns validator.
* </p>
* <p>
* The class lets you validate a scheduling pattern before/without using it with a {@link Scheduler} instance. Simply call:
* </p>
*
* <pre>
* boolean valid = SchedulingPatternValidator.validate(thePattern);
* </pre>
* <p>
* It is useful in validating user-entered patterns.
* </p>
* @author Carlo Pelliccia
* @deprecated Use {@link SchedulingPattern#validate(String)}.
*/
@Deprecated
public class SchedulingPatternValidator
{
/**
* Validates a string as a scheduling pattern.
* @param schedulingPattern The pattern to validate.
* @return true if the given string represents a valid scheduling pattern; false otherwise.
* @deprecated Use {@link SchedulingPattern#validate(String)}.
*/
@Deprecated
public static boolean validate(String schedulingPattern)
{
return SchedulingPattern.validate(schedulingPattern);
}
}

View File

@ -0,0 +1,107 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* This kind of task can be used to invoke a static method of a Java class. The specified method must accept an array of strings as its sole argument.
* @author Carlo Pelliccia
* @since 2.2
*/
class StaticMethodTask extends Task
{
/**
* The Java class name.
*/
private final String className;
/**
* The name of the static method of the class that has to be launched.
*/
private final String methodName;
/**
* Arguments for the static method. The array can be empty, but it can't be null.
*/
private final String[] args;
/**
* Builds the task.
* @param className The Java class name.
* @param methodName The name of the static method of the class that has to be launched.
* @param args Arguments for the static method. The array can be empty, but it can't be null.
*/
public StaticMethodTask(String className, String methodName, String[] args)
{
this.className = className;
this.methodName = methodName;
this.args = args;
}
/**
* Implements {@link Task#execute(TaskExecutionContext)}. It uses Java reflection to load the given class and call the given static method with the supplied arguments.
*/
@Override
public void execute(TaskExecutionContext context) throws RuntimeException
{
// Loads the class.
Class<?> classObject;
try
{
classObject = Class.forName(className);
}
catch (ClassNotFoundException e)
{
throw new RuntimeException("Cannot load class " + className, e);
}
// Finds the method.
Method methodObject;
try
{
Class<?>[] argTypes = new Class[]
{
String[].class
};
methodObject = classObject.getMethod(methodName, argTypes);
}
catch (NoSuchMethodException e)
{
throw new RuntimeException("Cannot find a " + methodName + "(String[]) method in class " + className, e);
}
int modifiers = methodObject.getModifiers();
if (!Modifier.isStatic(modifiers))
{
throw new RuntimeException("The method " + methodName + "(String[]) of the class " + className + " is not static");
}
// Invokes the method.
try
{
methodObject.invoke(null, new Object[]
{
args
});
}
catch (Exception e)
{
throw new RuntimeException("Failed to invoke the static method " + methodName + "(String[]) of the class " + className);
}
}
}

View File

@ -0,0 +1,213 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
/**
* <p>
* A package-reserved utility class. It spawns a secondary thread in which the supplied {@link InputStream} instance is read, and the incoming contents are written in the supplied {@link OutputStream}.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
class StreamBridge
{
/**
* Used to trace alive instances.
*/
static ArrayList<StreamBridge> traced = new ArrayList<>();
/**
* A self-referece, for inner classes.
*/
final StreamBridge myself = this;
/**
* The thread executing the job.
*/
private final Thread thread;
/**
* The stream from which the data is read.
*/
final InputStream in;
/**
* The stream in which the data is written.
*/
final OutputStream out;
/**
* Builds the instance.
* @param in The stream from which the data is read.
* @param out The stream in which the data is written.
*/
public StreamBridge(InputStream in, OutputStream out)
{
this.in = in;
this.out = out;
this.thread = new Thread(new Runner());
synchronized (traced)
{
traced.add(this);
}
}
/**
* Starts the bridge job.
*/
public void start()
{
thread.start();
}
/**
* Aborts the ongoing job.
*/
public void abort()
{
thread.interrupt();
try
{
out.close();
}
catch (Throwable t)
{
}
try
{
in.close();
}
catch (Throwable t)
{
}
}
/**
* Waits for this job to die.
* @throws InterruptedException If another thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
*/
public void join() throws InterruptedException
{
thread.join();
}
/**
* Waits at most <code>millis</code> milliseconds for this thread to die. A timeout of <code>0</code> means to wait forever.
* @param millis the time to wait in milliseconds.
* @throws InterruptedException If another thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
*/
public void join(long millis) throws InterruptedException
{
thread.join(millis);
}
/**
* @param millis the time to wait in milliseconds.
* @param nanos 0-999999 additional nanoseconds to wait.
* @throws IllegalArgumentException if the value of millis is negative the value of nanos is not in the range 0-999999.
* @throws InterruptedException If another thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
*/
public void join(long millis, int nanos) throws IllegalArgumentException, InterruptedException
{
thread.join(millis, nanos);
}
/**
* Tests if this bridge is alive. A job is alive if it has been started and has not yet completed.
* @return <code>true</code> if this thread is alive; <code>false</code> otherwise.
*/
public boolean isAlive()
{
return thread.isAlive();
}
/**
* Contains the routine doing the job in the secondary thread.
*/
private class Runner implements Runnable
{
public Runner()
{
}
@Override
public void run()
{
boolean skipout = false;
for (;;)
{
int b;
try
{
b = in.read();
}
catch (IOException e)
{
if (!Thread.interrupted())
{
e.printStackTrace();
}
break;
}
if (b == -1)
{
break;
}
if (!skipout)
{
try
{
out.write(b);
}
catch (IOException e)
{
if (!Thread.interrupted())
{
e.printStackTrace();
}
skipout = true;
}
}
}
try
{
out.close();
}
catch (Throwable t)
{
}
try
{
in.close();
}
catch (Throwable t)
{
}
synchronized (traced)
{
traced.remove(myself);
}
}
}
}

View File

@ -0,0 +1,144 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* <p>
* Abstract base representation of a cron4j task.
* </p>
* <p>
* Developers can extends this abstract class to build their own tasks.
* </p>
* <p>
* Extending Task means, above all, implementing the {@link Task#execute(TaskExecutionContext)} method. Within this method the task must perform its operation. If the <em>execute()</em> method returns regularly then the execution is considered to be successfully completed. If <em>execute()</em> dies
* throwing a {@link RuntimeException} then the task execution is considered to be failed. The supplied parameter, which is a {@link TaskExecutionContext} instance, helps the developer in integrating his task with the scheduler executor. Through the context the developer can check if the execution
* has been paused or stopped, and he can also push back some status informations by calling {@link TaskExecutionContext#setCompleteness(double)} and {@link TaskExecutionContext#setStatusMessage(String)}.
* </p>
* <p>
* If the custom task supports pausing, stopping and/or tracking, that should be notified by overriding {@link Task#canBePaused()}, {@link Task#canBeStopped()}, {@link Task#supportsCompletenessTracking()} and/or {@link Task#supportsStatusTracking()}.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
public abstract class Task
{
/**
* The ID for this task. Also used as an instance synchronization lock.
*/
private final Object id = GUIDGenerator.generate();
/**
* Empty constructor, does nothing.
*/
public Task()
{
}
/**
* It returns the ID for this task.
* @return The ID for this task.
*/
Object getId()
{
return id;
}
/**
* <p>
* Checks whether this task supports pause requests.
* </p>
* <p>
* Default implementation returns <em>false</em>.
* </p>
* <p>
* Task developers can override this method to let it return a <em>true</em> value, and at the same time they have to implement the {@link Task#execute(TaskExecutionContext)} method, so that pause requests are really handled. This can be done calling regularly the
* {@link TaskExecutionContext#pauseIfRequested()} method during the task execution.
* </p>
* @return true if this task can be paused; false otherwise.
*/
public boolean canBePaused()
{
return false;
}
/**
* <p>
* Checks whether this task supports stop requests.
* </p>
* <p>
* Default implementation returns <em>false</em>.
* </p>
* <p>
* Task developers can override this method to let it return a <em>true</em> value, and at the same time they have to implement the {@link Task#execute(TaskExecutionContext)} method, so that stop requests are really handled. This can be done checking regularly the
* {@link TaskExecutionContext#isStopped()} method during the task execution.
* </p>
* @return true if this task can be stopped; false otherwise.
*/
public boolean canBeStopped()
{
return false;
}
/**
* <p>
* Tests whether this task supports status tracking.
* </p>
* <p>
* Default implementation returns <em>false</em>.
* </p>
* <p>
* The task developer can override this method and returns <em>true</em>, having care to regularly calling the {@link TaskExecutionContext#setStatusMessage(String)} method during the task execution.
* </p>
* @return true if this task, during its execution, provides status message regularly.
*/
public boolean supportsStatusTracking()
{
return false;
}
/**
* <p>
* Tests whether this task supports completeness tracking.
* </p>
* <p>
* Default implementation returns <em>false</em>.
* </p>
* <p>
* The task developer can override this method and returns <em>true</em>, having care to regularly calling the {@link TaskExecutionContext#setCompleteness(double)} method during the task execution.
* </p>
* @return true if this task, during its execution, provides a completeness value regularly.
*/
public boolean supportsCompletenessTracking()
{
return false;
}
/**
* <p>
* This method is called to require a task execution, and should contain the core routine of any scheduled task.
* </p>
* <p>
* If the <em>execute()</em> method ends regularly the scheduler will consider the execution successfully completed, and this will be communicated to any {@link SchedulerListener} interested in it. If the <em>execute()</em> method dies throwing a {@link RuntimeException} the scheduler will
* consider it as a failure notification. Any {@link SchedulerListener} will be notified about the occurred exception.
* </p>
* @param context The execution context.
* @throws RuntimeException Task execution has somehow failed.
*/
public abstract void execute(TaskExecutionContext context) throws RuntimeException;
}

View File

@ -0,0 +1,37 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* <p>
* This interface describes a task collector. Task collectors can be registered in a {@link Scheduler} instance with the {@link Scheduler#addTaskCollector(TaskCollector)} method. Any registered task collector is queried by the scheduler once a minute. The developer has to implement the
* {@link TaskCollector#getTasks()} method, returning a {@link TaskTable} whose elements has been collected with a custom logic. In example the list can be extracted from a database.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
public interface TaskCollector
{
/**
* Once the instance has been registered on a {@link Scheduler} instance, with the {@link Scheduler#addTaskCollector(TaskCollector)} method, this method will be queried once a minute. It should return a custom {@link TaskTable} object. The scheduler instance will automatically iterate over the
* returned table elements, executing any task whose scheduling pattern is matching the current system time.
* @return The task table that will be automatically injected in the scheduler.
*/
public TaskTable getTasks();
}

View File

@ -0,0 +1,67 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* <p>
* A TaskExecutionContext object provides support methods for the execution of a task. An instance of this class is always passed to the task when its {@link Task#execute(TaskExecutionContext)} method is called. The task, while executing, can use the received context to exchange informations with
* its own executor. If the task declares to supports pausing, stopping, completeness tracking and/or status tracking, it has to use its context methods to perform any declared operation (checks pause and stop requests, sends back tracking informations).
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
public interface TaskExecutionContext
{
/**
* Returns the scheduler.
* @return The scheduler.
*/
public Scheduler getScheduler();
/**
* Returns the task executor.
* @return The task executor.
*/
public TaskExecutor getTaskExecutor();
/**
* Sets the current status tracking message, that has to be something about what the task is doing at the moment.
* @param message A message representing the current execution status. Null messages will be blanked.
*/
public void setStatusMessage(String message);
/**
* Sets the completeness tracking value, that has to be between 0 and 1.
* @param completeness A completeness value, between 0 and 1. Values out of range will be ignored.
*/
public void setCompleteness(double completeness);
/**
* If the task execution has been paused, stops until the operation is resumed. It can also returns because of a stop operation without any previous resuming. Due to this the task developer should always check the {@link TaskExecutionContext#isStopped()} value after any
* <em>pauseIfRequested()</em> call. Note that a task execution can be paused only if the task {@link Task#canBePaused()} method returns <em>true</em>.
*/
public void pauseIfRequested();
/**
* Checks whether the task execution has been demanded to be stopped. If the returned value is <em>true</em>, the task developer must shut down gracefully its task execution, as soon as possible. Note that a task execution can be stopped only if the task {@link Task#canBePaused()} method returns
* <em>true</em>.
* @return <em>true</em> if the current task execution has been demanded to be stopped; <em>false</em> otherwise.
*/
public boolean isStopped();
}

View File

@ -0,0 +1,612 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.util.ArrayList;
/**
* <p>
* Represents a task executor, which is something similar to threads.
* </p>
* <p>
* Each time a task is launched, a new executor is spawned, executing and watching the task
* </p>
* <p>
* Alive task executors can be retrieved with the {@link Scheduler#getExecutingTasks()} method, and they expose method to control the ongoing execution.
* </p>
* @see Scheduler#getExecutingTasks()
* @author Carlo Pelliccia
* @since 2.0
*/
public class TaskExecutor
{
/**
* The scheduler whose this executor belongs to.
*/
final Scheduler scheduler;
/**
* The executed task.
*/
final Task task;
/**
* A task execution context.
*/
final MyContext context;
/**
* A unique ID for this executor (used also as a lock object).
*/
private final String guid = GUIDGenerator.generate();
/**
* An alternative to this (inner classes need it).
*/
final TaskExecutor myself = this;
/**
* A list of {@link TaskExecutorListener} instances.
*/
private final ArrayList<TaskExecutorListener> listeners = new ArrayList<>();
/**
* A time stamp reporting the start time of this thread.
*/
long startTime = -1;
/**
* The thread actually executing the task.
*/
private Thread thread;
/**
* Is this executor paused now?
*/
boolean paused = false;
/**
* Has been this executor stopped?
*/
boolean stopped = false;
/**
* A lock object, for synchronization purposes.
*/
final Object lock = new Object();
/**
* Builds the executor.
* @param scheduler The scheduler whose this executor belongs to.
* @param task The task that has to be executed.
*/
TaskExecutor(Scheduler scheduler, Task task)
{
this.scheduler = scheduler;
this.task = task;
this.context = new MyContext();
}
/**
* Adds a listener to the executor.
* @param listener The listener.
*/
public void addTaskExecutorListener(TaskExecutorListener listener)
{
synchronized (listeners)
{
listeners.add(listener);
}
}
/**
* Removes a listener from the executor.
* @param listener The listener.
*/
public void removeTaskExecutorListener(TaskExecutorListener listener)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
/**
* Returns an array containing any {@link TaskExecutorListener} previously registered with the {@link TaskExecutor#addTaskExecutorListener(TaskExecutorListener)} method.
* @return An array containing any {@link TaskExecutorListener} previously registered with the {@link TaskExecutor#addTaskExecutorListener(TaskExecutorListener)} method.
*/
public TaskExecutorListener[] getTaskExecutorListeners()
{
synchronized (listeners)
{
int size = listeners.size();
TaskExecutorListener[] ret = new TaskExecutorListener[size];
for (int i = 0; i < size; i++)
{
ret[i] = listeners.get(i);
}
return ret;
}
}
/**
* Returns a GUID for this executor.
* @return A GUID for this executor.
*/
public String getGuid()
{
return guid;
}
/**
* Returns the {@link Scheduler} instance whose this executor belongs to.
* @return The scheduler.
*/
public Scheduler getScheduler()
{
return scheduler;
}
/**
* Returns the representation of the executed task.
* @return The executing/executed task.
*/
public Task getTask()
{
return task;
}
/**
* Returns a time stamp reporting the start time of this executor, or a value less than 0 if this executor has not been yet started.
* @return A time stamp reporting the start time of this executor, or a value less than 0 if this executor has not been yet started.
*/
public long getStartTime()
{
return startTime;
}
/**
* Checks whether this executor supports pausing.
* @return true if this executor supports pausing.
*/
public boolean canBePaused()
{
return task.canBePaused();
}
/**
* Checks whether this executor supports stopping.
* @return true if this executor supports stopping.
*/
public boolean canBeStopped()
{
return task.canBeStopped();
}
/**
* Checks whether this executor provides completeness tracking informations.
* @return true if this executor provides completeness tracking informations.
*/
public boolean supportsCompletenessTracking()
{
return task.supportsCompletenessTracking();
}
/**
* Checks whether this executor provides status tracking messages.
* @return true if this executor provides status tracking messages.
*/
public boolean supportsStatusTracking()
{
return task.supportsStatusTracking();
}
/**
* Starts executing the task (spawns a secondary thread).
* @param daemon true to spawn a daemon thread; false otherwise.
*/
void start(boolean daemon)
{
synchronized (lock)
{
startTime = System.currentTimeMillis();
String name = "cron4j::scheduler[" + scheduler.getGuid() + "]::executor[" + guid + "]";
thread = new Thread(new Runner());
thread.setDaemon(daemon);
thread.setName(name);
thread.start();
}
}
/**
* Pauses the ongoing execution.
* @throws UnsupportedOperationException The operation is not supported if {@link TaskExecutor#canBePaused()} returns <em>false</em>.
*/
public void pause() throws UnsupportedOperationException
{
if (!canBePaused())
{
throw new UnsupportedOperationException("Pause not supported");
}
synchronized (lock)
{
if ((thread != null) && !paused)
{
notifyExecutionPausing();
paused = true;
}
}
}
/**
* Resumes the execution after it has been paused.
*/
public void resume()
{
synchronized (lock)
{
if ((thread != null) && paused)
{
notifyExecutionResuming();
paused = false;
lock.notifyAll();
}
}
}
/**
* Stops the ongoing execution.
* @throws UnsupportedOperationException The operation is not supported if {@link TaskExecutor#canBeStopped()} returns <em>false</em>.
*/
public void stop() throws UnsupportedOperationException
{
if (!canBeStopped())
{
throw new UnsupportedOperationException("Stop not supported");
}
boolean joinit = false;
synchronized (lock)
{
if ((thread != null) && !stopped)
{
stopped = true;
if (paused)
{
resume();
}
notifyExecutionStopping();
thread.interrupt();
joinit = true;
}
}
if (joinit)
{
do
{
try
{
thread.join();
break;
}
catch (InterruptedException e)
{
continue;
}
}
while (true);
thread = null;
}
}
/**
* Waits for this executor to die.
* @throws InterruptedException If any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
*/
public void join() throws InterruptedException
{
if (thread != null)
{
thread.join();
}
}
/**
* Tests if this executor is alive. An executor is alive if it has been started and has not yet died.
* @return true if this executor is alive; false otherwise.
*/
public boolean isAlive()
{
if (thread != null)
{
return thread.isAlive();
}
return false;
}
/**
* Returns the current status message.
* @return The current status message.
* @throws UnsupportedOperationException The operation is not supported if {@link TaskExecutor#supportsStatusTracking()} returns <em>false</em>.
*/
public String getStatusMessage() throws UnsupportedOperationException
{
if (!supportsStatusTracking())
{
throw new UnsupportedOperationException("Status tracking not supported");
}
return context.getStatusMessage();
}
/**
* Returns the current completeness value, which is a value between 0 and 1.
* @return The current completeness value, which is a value between 0 and 1.
* @throws UnsupportedOperationException The operation is not supported if {@link TaskExecutor#supportsCompletenessTracking()} returns <em>false</em>.
*/
public double getCompleteness() throws UnsupportedOperationException
{
if (!supportsCompletenessTracking())
{
throw new UnsupportedOperationException("Completeness tracking not supported");
}
return context.getCompleteness();
}
/**
* Tests whether this executor has been paused.
* @return true if this executor is paused; false otherwise.
*/
public boolean isPaused()
{
return paused;
}
/**
* Tests whether this executor has been stopped.
* @return true if this executor is stopped; false otherwise.
*/
public boolean isStopped()
{
return stopped;
}
/**
* Notify registered listeners the execution has been paused.
*/
private void notifyExecutionPausing()
{
synchronized (listeners)
{
for (TaskExecutorListener taskExecutorListener : listeners)
{
TaskExecutorListener l = taskExecutorListener;
l.executionPausing(this);
}
}
}
/**
* Notify registered listeners the execution has been resumed.
*/
private void notifyExecutionResuming()
{
synchronized (listeners)
{
for (TaskExecutorListener taskExecutorListener : listeners)
{
TaskExecutorListener l = taskExecutorListener;
l.executionResuming(this);
}
}
}
/**
* Notify registered listeners the executor is stopping.
*/
private void notifyExecutionStopping()
{
synchronized (listeners)
{
for (TaskExecutorListener taskExecutorListener : listeners)
{
TaskExecutorListener l = taskExecutorListener;
l.executionStopping(this);
}
}
}
/**
* Notify registered listeners the execution has been terminated.
* @param exception If the execution has been terminated due to an error, this is the encountered exception; otherwise the parameter is null.
*/
void notifyExecutionTerminated(Throwable exception)
{
synchronized (listeners)
{
for (TaskExecutorListener taskExecutorListener : listeners)
{
TaskExecutorListener l = taskExecutorListener;
l.executionTerminated(this, exception);
}
}
}
/**
* Notify registered listeners the execution status message has changed.
* @param statusMessage The new status message.
*/
void notifyStatusMessageChanged(String statusMessage)
{
synchronized (listeners)
{
for (TaskExecutorListener taskExecutorListener : listeners)
{
TaskExecutorListener l = taskExecutorListener;
l.statusMessageChanged(this, statusMessage);
}
}
}
/**
* Notify registered listeners the execution completeness value has changed.
* @param completenessValue The new completeness value.
*/
void notifyCompletenessValueChanged(double completenessValue)
{
synchronized (listeners)
{
for (TaskExecutorListener taskExecutorListener : listeners)
{
TaskExecutorListener l = taskExecutorListener;
l.completenessValueChanged(this, completenessValue);
}
}
}
/**
* Inner Runnable class.
*/
private class Runner implements Runnable
{
public Runner()
{
}
/**
* It implements {@link Thread#run()}, executing the wrapped task.
*/
@Override
public void run()
{
Throwable error = null;
startTime = System.currentTimeMillis();
try
{
// Notify.
scheduler.notifyTaskLaunching(myself);
// Task execution.
task.execute(context);
// Succeeded.
scheduler.notifyTaskSucceeded(myself);
}
catch (Throwable exception)
{
// Failed.
error = exception;
scheduler.notifyTaskFailed(myself, exception);
}
finally
{
// Notify.
notifyExecutionTerminated(error);
scheduler.notifyExecutorCompleted(myself);
}
}
}
/**
* Inner TaskExecutionHelper implementation.
*/
private class MyContext implements TaskExecutionContext
{
/**
* Status message.
*/
private String message = "";
/**
* Completeness value.
*/
private double completeness = 0D;
public MyContext()
{
}
@Override
public Scheduler getScheduler()
{
return scheduler;
}
@Override
public TaskExecutor getTaskExecutor()
{
return myself;
}
@Override
public boolean isStopped()
{
return stopped;
}
@Override
public void pauseIfRequested()
{
synchronized (lock)
{
if (paused)
{
try
{
lock.wait();
}
catch (InterruptedException e)
{
}
}
}
}
@Override
public void setCompleteness(double completeness)
{
if ((completeness >= 0D) && (completeness <= 1D))
{
this.completeness = completeness;
notifyCompletenessValueChanged(completeness);
}
}
@Override
public void setStatusMessage(String message)
{
this.message = message != null ? message : "";
notifyStatusMessageChanged(message);
}
/**
* Returns the current status message.
* @return The current status message.
*/
public String getStatusMessage()
{
return message;
}
/**
* Returns the current completeness value, which is a value between 0 and 1.
* @return The current completeness value, which is a value between 0 and 1.
*/
public double getCompleteness()
{
return completeness;
}
}
}

View File

@ -0,0 +1,67 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* A TaskExecutorListener is notified with events from a {@link TaskExecutor}. You can add listeners to a TaskExecutor by calling its {@link TaskExecutor#addTaskExecutorListener(TaskExecutorListener)} method.
* @see TaskExecutor
* @author Carlo Pelliccia
* @since 2.0
*/
public interface TaskExecutorListener
{
/**
* Called when the execution has been requested to be paused.
* @param executor The source executor.
*/
public void executionPausing(TaskExecutor executor);
/**
* Called when the execution has been requested to be resumed.
* @param executor The source executor.
*/
public void executionResuming(TaskExecutor executor);
/**
* Called when the executor has been requested to be stopped.
* @param executor The source executor.
*/
public void executionStopping(TaskExecutor executor);
/**
* Called at execution end. If the execution has failed due to an error, the encountered exception is reported.
* @param executor The source executor.
* @param exception If the execution has been terminated due to an error, this is the encountered exception; otherwise the parameter is null.
*/
public void executionTerminated(TaskExecutor executor, Throwable exception);
/**
* Called every time the execution status message changes.
* @param executor The source executor.
* @param statusMessage The new status message.
*/
public void statusMessageChanged(TaskExecutor executor, String statusMessage);
/**
* Called every time the execution completeness value changes.
* @param executor The source executor.
* @param completenessValue The new completeness value.
*/
public void completenessValueChanged(TaskExecutor executor, double completenessValue);
}

View File

@ -0,0 +1,102 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
import java.util.ArrayList;
/**
* <p>
* A table coupling tasks with scheduling patterns.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
public class TaskTable
{
/**
* Table size.
*/
private int size = 0;
/**
* Pattern list.
*/
private final ArrayList<SchedulingPattern> patterns = new ArrayList<>();
/**
* Task list.
*/
private final ArrayList<Task> tasks = new ArrayList<>();
/**
* Adds a task and an associated scheduling pattern to the table.
* @param pattern The associated scheduling pattern.
* @param task The task.
*/
public void add(SchedulingPattern pattern, Task task)
{
patterns.add(pattern);
tasks.add(task);
size++;
}
/**
* Returns the size of the table, representing the number of the elements stored in it.
* @return The table size.
*/
public int size()
{
return size;
}
/**
* Returns the task at the specified position. Valid positions are between <em>0</em> to <em>{@link TaskTable#size()} - 1</em>.
* @param index The index.
* @return The task at the specified position.
* @throws IndexOutOfBoundsException If the supplied index is out of range.
*/
public Task getTask(int index) throws IndexOutOfBoundsException
{
return tasks.get(index);
}
/**
* Returns the scheduling pattern at the specified position. Valid positions are between <em>0</em> to <em>{@link TaskTable#size()} - 1</em>.
* @param index The index.
* @return The scheduling pattern at the specified position.
* @throws IndexOutOfBoundsException If the supplied index is out of range.
*/
public SchedulingPattern getSchedulingPattern(int index) throws IndexOutOfBoundsException
{
return patterns.get(index);
}
/**
* Remove a task from the table.
* @param index The index of the task to remove.
* @throws IndexOutOfBoundsException If the supplied index is not valid.
* @since 2.1
*/
public void remove(int index) throws IndexOutOfBoundsException
{
tasks.remove(index);
patterns.remove(index);
size--;
}
}

View File

@ -0,0 +1,117 @@
/*
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;
/**
* <p>
* TimeThreads are used by {@link Scheduler} instances. A TimerThread spends most of the time sleeping. It wakes up every minute and it requests to the scheduler the spawning of a {@link LauncherThread}.
* </p>
* @author Carlo Pelliccia
* @since 2.0
*/
class TimerThread extends Thread
{
/**
* A GUID for this object.
*/
private final String guid = GUIDGenerator.generate();
/**
* The owner scheduler.
*/
private Scheduler scheduler;
/**
* Builds the timer thread.
* @param scheduler The owner scheduler.
*/
public TimerThread(Scheduler scheduler)
{
this.scheduler = scheduler;
// Thread name.
String name = "cron4j::scheduler[" + scheduler.getGuid() + "]::timer[" + guid + "]";
setName(name);
}
/**
* Returns the GUID for this object.
* @return The GUID for this object.
*/
public Object getGuid()
{
return guid;
}
/**
* It has been reported that the {@link Thread#sleep(long)} method sometimes exits before the requested time has passed. This one offers an alternative that sometimes could sleep a few millis more than requested, but never less.
* @param millis The length of time to sleep in milliseconds.
* @throws InterruptedException If another thread has interrupted the current thread. The <i>interrupted status</i> of the current thread is cleared when this exception is thrown.
* @see Thread#sleep(long)
*/
private void safeSleep(long millis) throws InterruptedException
{
long done = 0;
do
{
long before = System.currentTimeMillis();
sleep(millis - done);
long after = System.currentTimeMillis();
done += (after - before);
}
while (done < millis);
}
/**
* Overrides {@link Thread#run()}.
*/
@Override
public void run()
{
// What time is it?
long millis = System.currentTimeMillis();
// Calculating next minute.
long nextMinute = ((millis / 60000) + 1) * 60000;
// Work until the scheduler is started.
for (;;)
{
// Coffee break 'till next minute comes!
long sleepTime = (nextMinute - System.currentTimeMillis());
if (sleepTime > 0)
{
try
{
safeSleep(sleepTime);
}
catch (InterruptedException e)
{
// Must exit!
break;
}
}
// What time is it?
millis = System.currentTimeMillis();
// Launching the launching thread!
scheduler.spawnLauncher(millis);
// Calculating next minute.
nextMinute = ((millis / 60000) + 1) * 60000;
}
// Discard scheduler reference.
scheduler = null;
}
}

View File

@ -1,18 +1,20 @@
/*
* This file is part of the L2J Mobius project.
* cron4j - A pure Java cron-like scheduler
*
* Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.util.cron4j;