001 package tynamo_watchdog;
002
003 import java.io.IOException;
004 import java.util.ArrayList;
005 import java.util.Arrays;
006 import java.util.Date;
007 import java.util.List;
008 import java.util.Properties;
009
010 import javax.mail.Message;
011 import javax.mail.MessagingException;
012 import javax.mail.Session;
013 import javax.mail.Transport;
014 import javax.mail.internet.InternetAddress;
015 import javax.mail.internet.MimeMessage;
016
017 public class Watchdog {
018 public static final String SMTP_HOST = "smtp.host";
019 public static final String SMTP_PORT = "smtp.port";
020 public static final String SEND_EMAIL = "watchdog.sendemail";
021 public static final String EMAIL_PATH = "watchdog.emailpath";
022 public static final String COMMAND = "watchdog.command";
023 public static final String KEEPALIVE_INTERVAL = "watchdog.keepalive";
024 public static final String FINALALARM_DELAY = "watchdog.alarmdelay";
025
026 public static final String STOP_MESSAGE = Watchdog.class.getSimpleName();
027
028 private String emailRecipient;
029 private String smtpHost;
030 private Integer smtpPort;
031 private String appName;
032 private String hostname;
033
034 private long lastOk;
035 private long keepAliveInterval = 5000L;
036 private long finalAlarmDelay = 60000L;
037 private boolean warningSent;
038
039 public Watchdog(String appName, String emailRecipient, String smtpHost, String smtpPort, Long keepAliveInterval, Long finalAlarmDelay) {
040 this.appName = appName;
041 this.emailRecipient = emailRecipient;
042 this.smtpHost = smtpHost;
043 if (keepAliveInterval != null) this.keepAliveInterval = keepAliveInterval;
044 if (finalAlarmDelay != null) this.finalAlarmDelay = finalAlarmDelay;
045 // FIXME catch numberFormatException
046 this.smtpPort = smtpPort == null ? null : Integer.valueOf(smtpPort);
047 hostname = System.getenv("HOSTNAME");
048 if (hostname == null) hostname = "localhost.localdomain";
049 lastOk = System.currentTimeMillis();
050 }
051
052 public static void main(String[] args) throws Exception {
053 // With no arguments, print out the help and exit
054 List<String> arguments = new ArrayList<String>(Arrays.asList(args));
055
056 if (args.length <= 0 || arguments.contains("--help")) {
057 System.out.println("Tynamo watchdog. This application is designed to run as a child process ");
058 return;
059 }
060
061 String appName = args.length > 0 ? args[0] : "dev/exploded";
062 sleep(5000);
063 String value = System.getProperty(KEEPALIVE_INTERVAL);
064 Long keepAliveInterval = null;
065 try {
066 keepAliveInterval = Long.valueOf(value);
067 } catch (NumberFormatException e) {
068 }
069 value = System.getProperty(FINALALARM_DELAY);
070 Long finalAlarmDelay = null;
071 try {
072 finalAlarmDelay = Long.valueOf(value);
073 } catch (NumberFormatException e) {
074 }
075
076 Watchdog watchdog = new Watchdog(appName, System.getProperty(SEND_EMAIL), System.getProperty(SMTP_HOST), System.getProperty(SMTP_PORT),
077 keepAliveInterval, finalAlarmDelay);
078 watchdog.go();
079 }
080
081 public void go() {
082 try {
083 while (lastOk + finalAlarmDelay > System.currentTimeMillis())
084 makeRounds();
085 } catch (IOException e) {
086 System.err.println("Parent process stopped at " + (new Date()));
087 }
088 // Exited either because of exception thrown or because exceeded finalAlarmDelay
089 // BY default, send the application lost email. makeRounds() will System.exit immediately if STOP_MESSAGE is
090 // received
091 sendApplicationLostEmail();
092 }
093
094 private static void sleep(long millis) {
095 try {
096 Thread.sleep(millis);
097 } catch (InterruptedException e) {
098 }
099 }
100
101 /**
102 * makeRounds() will make system exit immediately if STOP_MESSAGE is received
103 *
104 * @throws IOException
105 */
106 public void makeRounds() throws IOException {
107 int available = 0;
108 byte[] bytes = new byte[STOP_MESSAGE.getBytes().length];
109
110 while ((available = System.in.available()) > 0) {
111 if (available >= STOP_MESSAGE.getBytes().length) System.exit(0);
112 // skip() didn't seem to work for standard input
113 System.in.read(bytes, 0, available);
114 lastOk = System.currentTimeMillis();
115 warningSent = false;
116 // Normally, read at half the rate of the writes
117 sleep(keepAliveInterval * 2);
118 }
119 // Send the first warning
120 if (!warningSent) sendRunningSlowEmail();
121 warningSent = true;
122 sleep(keepAliveInterval);
123 }
124
125 void sendRunningSlowEmail() {
126 String subject = "Application " + appName + " is running slow!";
127 StringBuilder sb = new StringBuilder();
128 sb.append("Master application '");
129 sb.append(appName);
130 sb.append("' at ");
131 sb.append(hostname);
132 sb.append(" has missed sending some alive signals. The last OK was received at ");
133 sb.append(new Date(lastOk));
134 sb.append(". \n");
135 sb.append("This may indicate the application has dead-locked, been unexpectedly terminated or is running out of resources. \n");
136 sb.append("Action taken: email sent to '");
137 sb.append(emailRecipient);
138 sb.append("', still monitoring\n");
139 try {
140 sendEmail(subject, sb);
141 } catch (MessagingException e) {
142 System.err.println("Couldn't send warning email because of: " + e.getMessage());
143 }
144 }
145
146 void sendApplicationLostEmail() {
147 String subject = "Application " + appName + " has failed!";
148 StringBuilder sb = new StringBuilder();
149 sb.append("Master application '");
150 sb.append(appName);
151 sb.append("' at ");
152 sb.append(hostname);
153 sb.append(" was lost at ");
154 sb.append(new Date());
155 sb.append("\n");
156 sb.append("Action taken: email sent to '");
157 sb.append(emailRecipient);
158 sb.append("'\n");
159
160 try {
161 sendEmail(subject, sb);
162 } catch (MessagingException e) {
163 // TODO Auto-generated catch block
164 e.printStackTrace();
165 }
166 }
167
168 boolean sendEmail(String subject, StringBuilder content) throws MessagingException {
169 if (emailRecipient == null || emailRecipient.isEmpty()) return false;
170 System.out.println("Sending email to: " + emailRecipient + " " + System.getProperty(SMTP_PORT));
171 boolean debug = false;
172
173 // Set the host smtp address
174 Properties props = new Properties();
175 props.put("mail.smtp.host", smtpHost);
176 props.put("mail.smtp.port", String.valueOf(smtpPort));
177 props.put("mail.smtp.debug", "true");
178
179 // create some properties and get the default Session
180 Session session = Session.getDefaultInstance(props, null);
181 session.setDebug(debug);
182
183 // create a message
184 Message msg = new MimeMessage(session);
185
186 // set the from and to addresses
187 InternetAddress addressFrom = new InternetAddress("watchdog@" + hostname);
188 msg.setFrom(addressFrom);
189
190 InternetAddress[] addressTo = new InternetAddress[1];
191 addressTo[0] = new InternetAddress(emailRecipient);
192 msg.setRecipients(Message.RecipientType.TO, addressTo);
193
194 msg.setSubject(subject);
195 msg.setText(content.toString());
196 Transport.send(msg);
197 return true;
198 }
199 }