package zeta.tool;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Date;
import zeta.util.DatabaseUtils;
import zeta.util.ProcessUtils;
import zeta.util.Properties;
import zeta.util.StreamUtils;
import zeta.util.StringUtils;
import zeta.util.ThrowableHandler;
public class KeepAlive {
public static void main(String[] args) {
try {
if (args.length == 1) {
if (args[0].equals("r")) {
Properties properties = new Properties();
new KeepAlive(null).checkPage("POST", properties.get("reload.host", ""), properties.get("reload.url", ""), false, null);
} else if (args[0].equals("?")) {
System.out.println("USAGE: [r] [y/n]");
} else {
new KeepAlive(args[0]);
}
} else {
new KeepAlive(null);
}
} catch (Exception e) {
ThrowableHandler.handle(e);
}
}
public KeepAlive(String arg0) throws IOException {
Properties properties = new Properties();
if (properties.get("keep_alive.database.restart.sleep") != null) {
keepAliveDatabase = new KeepAliveDatabase();
keepAliveDatabase.start();
}
if (properties.get("keep_alive.connection") != null) {
keepAliveConnection = new KeepAliveConnection(arg0);
keepAliveConnection.start();
}
if (properties.get("keep_alive.tomcat") != null) {
new KeepAliveTomcat().start();
}
}
static String getPPPClientAddress() {
BufferedReader reader = null;
try {
Process process = Runtime.getRuntime().exec("ipconfig");
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
boolean found = false;
while (true) {
String s = reader.readLine();
if (s == null) {
break;
}
if (s.startsWith("PPP")) {
found = true;
}
if (found && s.indexOf("ateway") >= 0) {
int idx = s.indexOf(':');
if (idx >= 0 && idx < s.length()-2) {
return s.substring(idx+2);
}
}
}
process.waitFor();
} catch (InterruptedException ie) {
} catch (IOException ioe) {
} finally {
StreamUtils.close(reader);
}
return null;
}
private void ping() {
BufferedReader reader = null;
try {
if (++pingIdx >= pingAddresses.length) {
pingIdx = 0;
}
Process process = Runtime.getRuntime().exec(new String[] { "ping", pingAddresses[pingIdx] });
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
while (reader.readLine() != null) {
}
process.waitFor();
} catch (InterruptedException ie) {
} catch (IOException ioe) {
} finally {
StreamUtils.close(reader);
}
}
private static boolean checkPage(String requestMethod, String host, String page, boolean verbose, PrintStream printInput) {
if (host != null && page != null && host.length() > 0) {
if (verbose) {
trace("check " + host, true, true);
}
HttpURLConnection connection = null;
try {
URL url = new URL("http", host, 80, page);
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod(requestMethod);
connection.setUseCaches(false);
connection.connect();
if (printInput != null) {
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
StreamUtils.writeData(connection.getInputStream(), printInput, false, false);
return true;
} else {
return false;
}
}
return (connection.getResponseCode() == HttpURLConnection.HTTP_OK);
} catch (UnknownHostException uhe) {
ThrowableHandler.handle(uhe);
} catch (MalformedURLException e) {
ThrowableHandler.handle(e);
} catch (IOException e) {
ThrowableHandler.handle(e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
return false;
}
private void sendURL(String address) throws IOException {
trace("send URL: " + address);
Properties properties = new Properties();
String tmpDir = properties.get("tmp_dir", "tmp");
while (true) {
new File(tmpDir + "/database.connection.url").delete();
Writer writer = null;
BufferedReader reader = null;
FTP ftp = null;
try {
String filename = properties.get("database.connection.url", ".") + "/database.connection.url";
writer = new FileWriter(filename);
writer.write("jdbc:db2://" + address + ":8082/zeta");
writer.close();
writer = null;
ftp = new FTP();
ftp.connect(properties.get("ftp.host", ""));
ftp.login(properties.get("ftp.user", ""), properties.get("ftp.password", ""));
ftp.cd(properties.get("ftp.config.path", "."));
ftp.put(filename, "database.connection.url", FTP.MODE_BINARY);
ftp.get("database.connection.url", tmpDir + "/database.connection.url", FTP.MODE_BINARY);
boolean ok = false;
reader = new BufferedReader(new FileReader(tmpDir + "/database.connection.url"));
while (!ok) {
String s = reader.readLine();
if (s == null) {
break;
}
ok = (s.indexOf(address) >= 0);
}
reader.close();
reader = null;
if (ok) {
String reloadHost = properties.get("reload.host");
String reloadURL = properties.get("reload.url", "");
if (reloadHost != null && reloadURL != null) {
checkPage("POST", reloadHost, reloadURL, false, System.out);
}
return;
} else {
System.err.println("Error: new address not in file database.connection.url");
}
} catch (IOException ioe) {
} catch (Exception e) {
ThrowableHandler.handle(e);
} finally {
StreamUtils.close(reader);
StreamUtils.close(writer);
if (ftp != null) {
ftp.disconnect();
}
}
trace(" error to send URL");
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
}
private static void trace(String text) {
trace(text, true, true);
}
private static void trace(String text, boolean timestamp, boolean newLine) {
if (timestamp) {
if (newLine) {
System.out.println(formatter.format(new Date()) + text);
} else {
System.out.print(formatter.format(new Date()) + text);
System.out.flush();
}
} else {
if (newLine) {
System.out.println(text);
} else {
System.out.print(text);
System.out.flush();
}
}
}
private InetAddress address = null;
private final static String[] pingAddresses = { "www.zetagrid.net", "www.ibm.com", "www.hipilib.de", "www.google.com", "www.yahoo.com" };
private int pingIdx = 0;
private ServerSocket serverSocket = null;
private Throwable serverSocketThrowable = null;
private String previousHostAddress = "";
private static SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss ");
class KeepAliveConnection extends Thread {
boolean activeReconnect = false;
boolean traceTimeConnected = false;
long lastConnection = System.currentTimeMillis();
String arg0;
KeepAliveConnection(String arg0) {
this.arg0 = arg0;
}
public void run() {
boolean firstTime = true;
trace("Keep alive connection");
while (true) {
try {
final Properties properties = new Properties();
final long restartSleep = properties.get("keep_alive.restart.sleep", 0);
if (firstTime && restartSleep > 0) {
char ch = ' ';
if (arg0 != null && arg0.length() > 0) {
ch = arg0.charAt(0);
} else {
System.out.println("Do you want to reconnect now? [Y/N]");
ch = (char)System.in.read();
}
if (ch == 'Y' || ch == 'y') {
lastConnection = 0;
}
}
long time = System.currentTimeMillis()-lastConnection;
if (restartSleep > 0 && time >= restartSleep) {
trace("address will be changed now");
if (keepAliveDatabase != null) {
synchronized (keepAliveDatabase) {
keepAliveDatabase.lastDBReset = keepAliveDatabase.dbRestart("db2 stop since address will be changed", false);
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
Thread t = new Thread() {
public void run() {
String command = properties.get("keep_alive.disconnect.cmd");
try {
for (int idx = 1; command != null; ++idx) {
trace("command: " + command);
ProcessUtils.exec(command, null, false, 5000);
trace("executed");
try {
Thread.sleep(3000);
} catch (InterruptedException ie) {
}
command = properties.get("keep_alive.connection.cmd." + idx);
}
} finally {
activeReconnect = false;
}
}
};
activeReconnect = true;
t.start();
for (int wait = 0; wait < 15 && activeReconnect || wait < 30 && getPPPClientAddress() == null; ++wait) {
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
} else if (restartSleep > 0 && time+3600000 >= restartSleep) {
trace("address will be changed in about " + ((restartSleep-time)/60000) + " minutes");
}
int wait = 0;
long connectionTime = properties.get("keep_alive.reboot.wait", 0);
if (connectionTime > 0) {
connectionTime += System.currentTimeMillis();
}
while (getPPPClientAddress() == null) {
if (++wait == 10) {
trace(".", false, false);
if (System.currentTimeMillis() > connectionTime) {
String cmd = properties.get("keep_alive.reboot.cmd", null);
if (cmd != null) {
ProcessUtils.exec(cmd);
System.exit(1);
}
}
if (!activeReconnect && properties.get("keep_alive.connection.cmd.1") != null) {
activeReconnect = true;
for (int idx = 1;; ++idx) {
final String command = properties.get("keep_alive.connection.cmd." + idx);
if (command == null) {
break;
} else {
Thread t = new Thread() {
public void run() {
try {
ProcessUtils.exec(command, null, false, 5000);
} finally {
activeReconnect = false;
}
}
};
t.start();
}
}
}
for (int i = 0; i < 10 && activeReconnect; ++i) {
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
wait = 0;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
}
address = InetAddress.getByName(getPPPClientAddress());
serverSocket = null;
serverSocketThrowable = null;
Thread t = new Thread() {
public void run() {
try {
trace("socket ", true, false);
serverSocket = new ServerSocket(SOCKET_PORT, 50, address);
trace(serverSocket.getInetAddress().getHostAddress() + ':' + SOCKET_PORT + ' ', false, false);
if (!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
trace("connected", traceTimeConnected, true);
traceTimeConnected = false;
for (int i = 0; i < 10; ++i) {
try {
Thread.sleep(120*1000);
} catch (InterruptedException ie) {
}
ping();
}
socket.getOutputStream().write(-1);
}
} catch (Throwable e) {
serverSocketThrowable = e;
ThrowableHandler.handle(e);
}
}
};
t.start();
do {
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
} while (serverSocket == null && serverSocketThrowable == null);
if (!previousHostAddress.equals(address.getHostAddress())) {
lastConnection = System.currentTimeMillis();
String s = address.getHostAddress();
if (firstTime || previousHostAddress.length() > 0 || !checkPage("GET", properties.get("zetagrid.check.host", "www.zetagrid.net"), properties.get("zetagrid.check.url", "/servlet/service/statistic"), true, null)) {
int sleep = 60000;
while (true) {
sendURL(s);
if (keepAliveDatabase != null) {
synchronized (keepAliveDatabase) {
keepAliveDatabase.lastDBReset = keepAliveDatabase.dbRestart("db2 start since address changed", true);
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
if (checkPage("GET", properties.get("zetagrid.check.host", "www.zetagrid.net"), properties.get("zetagrid.check.url", "/servlet/service/statistic"), true, null)) {
if (firstTime) {
String cmd = properties.get("keep_alive.daemon.cmd", null);
if (cmd != null) {
trace("exec daemon: " + cmd);
Runtime.getRuntime().exec(cmd);
}
firstTime = false;
}
traceTimeConnected = true;
break;
} else {
try {
Thread.sleep(sleep);
} catch (InterruptedException ie) {
}
sleep = (sleep == 60000)? 900000 : 60000;
}
address = InetAddress.getByName(getPPPClientAddress());
s = address.getHostAddress();
}
}
previousHostAddress = s;
}
Socket socket = new Socket();
socket.bind(new InetSocketAddress(0));
socket.connect(new InetSocketAddress(address, SOCKET_PORT), 20*3600*1000);
socket.getInputStream().read();
socket.close();
serverSocket.close();
serverSocket = null;
} catch (IOException ioe) {
ThrowableHandler.handle(ioe);
}
if (serverSocket != null) {
try {
serverSocket.close();
serverSocket = null;
} catch (IOException ioe) {
ThrowableHandler.handle(ioe);
}
}
if (getPPPClientAddress() == null) {
trace("reconnect.");
}
}
}
}
class KeepAliveDatabase extends Thread {
public void run() {
trace("Keep alive database");
try {
Thread.sleep(10000);
} catch (InterruptedException ie) {
}
try {
Properties properties = new Properties();
Class.forName(properties.get("db_driver", "COM.ibm.db2.jdbc.app.DB2Driver")).newInstance();
int sleep = properties.get("keep_alive.database.check.sleep", 40000);
int sleepDBReset = properties.get("keep_alive.database.restart.sleep", 3600000);
ByteArrayOutputStream out = new ByteArrayOutputStream(20000);
while (true) {
try {
if (keepAliveConnection == null || previousHostAddress.equals(InetAddress.getByName(getPPPClientAddress()).getHostAddress())) {
synchronized (keepAliveDatabase) {
if (con == null) {
con = DriverManager.getConnection(properties.get("db_url", "jdbc:db2:zeta"), properties.get("db_user", ""), properties.get("db_password", ""));
stmt = con.createStatement();
}
stmt.executeQuery("SELECT id FROM zeta.task WHERE id=1").close();
}
}
} catch (SQLException se) {
DatabaseUtils.close(stmt);
stmt = null;
DatabaseUtils.close(con);
con = null;
trace(se.getMessage());
synchronized (keepAliveDatabase) {
if (System.currentTimeMillis()-lastDBReset >= 60000) {
lastDBReset = dbRestart("db2 restart since an exception occurred", true);
}
}
} catch (Exception e) {
trace(e.getMessage());
ThrowableHandler.handle(e);
}
synchronized (keepAliveDatabase) {
if (System.currentTimeMillis()-lastDBReset >= sleepDBReset) {
lastDBReset = dbRestart("db2 restart", true);
}
}
try {
Thread.sleep(sleep);
} catch (InterruptedException ie) {
}
}
} catch (Exception e) {
ThrowableHandler.handle(e);
} finally {
DatabaseUtils.close(stmt);
DatabaseUtils.close(con);
trace("Stop to keep alive database");
}
}
private long dbRestart(String text, boolean start) {
boolean killDB = true;
for (int restart = 0; restart < 3 && killDB; ++restart) {
killDB = false;
DatabaseUtils.close(stmt);
stmt = null;
DatabaseUtils.close(con);
con = null;
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(5000);
Thread thread = new Thread() {
public void run() {
ProcessUtils.exec("db2stop force");
}
};
dbForceApplicationsAll();
trace(text);
thread.start();
try {
long startTime = System.currentTimeMillis();
while (true) {
out.reset();
if (ProcessUtils.exec("pslist", out, true) == 0) {
String s = out.toString();
int idx = s.indexOf("db2syscs ");
if (idx < 0 && s.indexOf("db2stop ") < 0) {
break;
}
int idx2 = s.indexOf(':', idx); int l = s.length();
while (--idx2 > idx && s.charAt(idx2) != ' ');
while (--idx2 > idx && !Character.isDigit(s.charAt(idx2)));
for (idx = idx2-1; idx > 0 && Character.isDigit(s.charAt(idx)); --idx);
long mem = Long.parseLong(s.substring(idx+1, idx2+1));
trace("db2syscs mem: " + mem);
long timeDiff = System.currentTimeMillis()-startTime;
if (mem < 90000 && timeDiff >= 90000 || timeDiff >= 300000) {
break;
}
} else {
trace("Error: result of pslist is not valid");
System.err.println(out.toString());
}
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
out.reset();
if (ProcessUtils.exec("pslist", out, true) == 0) {
String s = out.toString();
int idx = s.indexOf("db2syscs "); if (idx >= 0) {
idx += 8;
int l = s.length();
while (idx < l && !Character.isDigit(s.charAt(idx))) {
++idx;
}
int idx2 = idx+1;
while (idx2 < l && Character.isDigit(s.charAt(idx2))) {
++idx2;
}
int pid = Integer.parseInt(s.substring(idx, idx2));
trace("db2syscs: " + pid);
out.reset();
ProcessUtils.exec("pskill " + pid, out, true);
s = out.toString();
System.out.println(s);
killDB = (StringUtils.indexOfIgnoreCase(s, "killed", 0) >= 0);
}
}
} catch (Exception e) {
ThrowableHandler.handle(e);
}
out.reset();
if (ProcessUtils.exec("pslist", out, true) == 0) {
try {
String s = out.toString();
for (int idx = s.indexOf("db2fmp "); idx >= 0; idx = s.indexOf("db2fmp ", idx+1)) {
idx += 6;
int l = s.length();
while (idx < l && !Character.isDigit(s.charAt(idx))) {
++idx;
}
int idx2 = idx+1;
while (idx2 < l && Character.isDigit(s.charAt(idx2))) {
++idx2;
}
int pid = Integer.parseInt(s.substring(idx, idx2));
trace("db2fmp: " + pid);
ProcessUtils.exec("pskill " + pid, System.out, true);
}
if (killDB) {
ProcessUtils.exec("net stop \"DB2 Remote Command Server\"");
for (int idx = s.indexOf("db2rcmd "); idx >= 0; idx = s.indexOf("db2rcmd ", idx+1)) {
idx += 7;
int l = s.length();
while (idx < l && !Character.isDigit(s.charAt(idx))) {
++idx;
}
int idx2 = idx+1;
while (idx2 < l && Character.isDigit(s.charAt(idx2))) {
++idx2;
}
int pid = Integer.parseInt(s.substring(idx, idx2));
trace("db2rcmd: " + pid);
ProcessUtils.exec("pskill " + pid, System.out, true);
}
}
} catch (Exception e) {
ThrowableHandler.handle(e);
}
}
try {
ProcessUtils.exec("db2stop");
} catch (Exception e) {
}
} finally {
if (start) {
ProcessUtils.exec("net start \"DB2 Remote Command Server\"");
ProcessUtils.exec("db2start");
}
}
try {
Thread.sleep(restart*10000);
} catch (InterruptedException ie) {
}
}
return System.currentTimeMillis();
}
private long dbForceApplicationsAll() {
try {
trace("db2 force application all");
Properties properties = new Properties();
String syncFile = properties.get("temp_dir", "") + "sync.tmp";
File sync = new File(syncFile);
sync.delete();
ProcessUtils.exec("db2start");
DatabaseUtils.db2CLP("FORCE APPLICATION ALL;\nCONNECT TO zeta;\nEXPORT TO " + syncFile + " OF DEL SELECT server_id FROM zeta.error WHERE 1=0;\nCONNECT RESET;\nTERMINATE;\n");
for (int i = 0; i < 240 && !sync.exists(); ++i) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
sync.delete();
} catch (Exception e) {
ThrowableHandler.handle(e);
}
return System.currentTimeMillis();
}
private long lastDBReset = 0;
private Connection con = null;
private Statement stmt = null;
}
class KeepAliveTomcat extends Thread {
public void run() {
trace("Keep alive Tomcat");
try {
String lastOut = "";
while (true) {
ByteArrayOutputStream out = new ByteArrayOutputStream(500);
ProcessUtils.exec("net start tomcat", out, false);
StreamUtils.close(out);
String s = new String(out.toByteArray());
if (!s.equals(lastOut)) {
lastOut = s;
trace(s, false, true);
trace(" tomcat restart");
}
try {
Thread.sleep(20000);
} catch (InterruptedException ie) {
}
}
} catch (Exception e) {
ThrowableHandler.handle(e);
}
}
}
private KeepAliveDatabase keepAliveDatabase = null;
private KeepAliveConnection keepAliveConnection = null;
private final static int SOCKET_PORT = 20000;
}