package com.silicondust.libhdhomerun; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.Date; public class HDHomerun_Debug { private static final String HDHOMERUN_DEBUG_HOST = "debug.silicondust.com"; private static final int HDHOMERUN_DEBUG_PORT = 8002; private static final int HDHOMERUN_DEBUG_CONNECT_RETRY_TIME = 30000; private static final int HDHOMERUN_DEBUG_CONNECT_TIMEOUT = 10000; private static final int HDHOMERUN_DEBUG_SEND_TIMEOUT = 10000; class hdhomerun_debug_message_t { hdhomerun_debug_message_t next; hdhomerun_debug_message_t prev; String buffer; }; private class hdhomerun_debug_t { public hdhomerun_debug_t() { sock = null; print_lock = new Mutex(); queue_lock = new Mutex(); send_lock = new Mutex(); enabled = false; terminate = false; prefix = ""; file_name = ""; file_fp = null; queue_head = null; queue_tail = null; queue_depth = 0; if(mDbg == null) mDbg = this; thread = new DebugThread(); thread.start(); } DebugThread thread; volatile boolean enabled; volatile boolean terminate; String prefix; Mutex print_lock; Mutex queue_lock; Mutex send_lock; hdhomerun_debug_message_t queue_head; hdhomerun_debug_message_t queue_tail; int queue_depth; long connect_delay; String file_name; FileOutputStream file_fp; HDHomerun_Sock sock; }; static hdhomerun_debug_t mDbg = null; static Integer base = new Integer(0); public HDHomerun_Debug() { synchronized(base) { if(mDbg == null) mDbg = new hdhomerun_debug_t(); } } synchronized public void destroy() { if (mDbg == null) { return; } mDbg.terminate = true; try { mDbg.thread.join(); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if (mDbg.prefix != null) mDbg.prefix = null; if (mDbg.file_name != null) mDbg.file_name = null; if (mDbg.file_fp != null) { try { mDbg.file_fp.close(); } catch (IOException e) { e.printStackTrace(); } mDbg.file_fp = null; } if (mDbg.sock != null) mDbg.sock.destroy(); mDbg = null; } /* Send lock held by caller */ private void close_internal() { if (mDbg.file_fp != null) { try { mDbg.file_fp.close(); } catch (IOException e) { e.printStackTrace(); } mDbg.file_fp = null; } if (mDbg.sock != null) { mDbg.sock.destroy(); mDbg.sock = null; } } public void close(int timeout) { if (mDbg == null) { return; } if (timeout > 0) { flush(timeout); } mDbg.send_lock.lock(); close_internal(); mDbg.connect_delay = 0; mDbg.send_lock.unlock(); } public void set_filename(final String filename) { if (mDbg == null) { return; } mDbg.send_lock.lock(); if (null == filename && null == mDbg.file_name) { mDbg.send_lock.unlock(); return; } if (null != filename && null != mDbg.file_name) { if (filename.compareTo(mDbg.file_name) == 0) { mDbg.send_lock.unlock(); return; } } close_internal(); mDbg.connect_delay = 0; if (mDbg.file_name != null) mDbg.file_name = null; if (null != filename) mDbg.file_name = new String(filename); mDbg.send_lock.unlock(); } public void set_prefix(final String prefix) { if (null == mDbg) { return; } mDbg.print_lock.lock(); mDbg.prefix = null; if (null !=prefix) { mDbg.prefix = new String(prefix); } mDbg.print_lock.unlock(); } public void enable() { if (mDbg == null) { return; } mDbg.enabled = true; } public void disable() { if (mDbg == null) { return; } mDbg.enabled = false; } public boolean enabled() { if (mDbg == null) { return false; } return mDbg.enabled; } public void flush(long timeout) { if (null == mDbg) { return; } timeout = HDHomerun_OS.getcurrenttime() + timeout; while (HDHomerun_OS.getcurrenttime() < timeout) { mDbg.queue_lock.lock(); hdhomerun_debug_message_t message = mDbg.queue_tail; mDbg.queue_lock.unlock(); if (null == message) { return; } HDHomerun_OS.msleep_approx(10); } } public void printf(final String msg) { if (mDbg == null) { return; } if (!mDbg.enabled) { return; } hdhomerun_debug_message_t message = new hdhomerun_debug_message_t(); if (null == message) { return; } /* * Timestamp. */ Date now = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-hh:mm:ss"); String strBuff = sdf.format(now) + " "; /* * Debug prefix. */ mDbg.print_lock.lock(); if (null != mDbg.prefix) { strBuff += mDbg.prefix + " "; } mDbg.print_lock.unlock(); /* * Message text. */ strBuff += msg; /* * Force newline. */ strBuff += "\n"; /* * Enqueue. */ mDbg.queue_lock.lock(); message.prev = null; message.next = mDbg.queue_head; mDbg.queue_head = message; if (message.next != null ) { message.next.prev = message; } else { mDbg.queue_tail = message; } mDbg.queue_depth++; mDbg.queue_lock.unlock(); } /* Send lock held by caller */ private boolean output_message_file(hdhomerun_debug_message_t message) { if (null == mDbg.file_fp) { long current_time = HDHomerun_OS.getcurrenttime(); if (current_time < mDbg.connect_delay) { return false; } mDbg.connect_delay = current_time + 30*1000; try { mDbg.file_fp = new FileOutputStream(mDbg.file_name, true); } catch (FileNotFoundException e) { e.printStackTrace(); } if (null == mDbg.file_fp) { return false; } } try { mDbg.file_fp.write(message.buffer.getBytes("UTF8")); mDbg.file_fp.write(0); mDbg.file_fp.flush(); } catch (IOException e) { e.printStackTrace(); } return true; } /* Send lock held by caller */ private boolean output_message_sock(hdhomerun_debug_message_t message) { if (mDbg.sock == null) { long current_time = HDHomerun_OS.getcurrenttime(); if (current_time < mDbg.connect_delay) { return false; } mDbg.connect_delay = (int) (current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME); int remote_addr = HDHomerun_Sock.getaddrinfo_addr(HDHOMERUN_DEBUG_HOST); if(remote_addr == 0) { close_internal(); return false; } try { mDbg.sock = new HDHomerun_Sock(remote_addr, HDHOMERUN_DEBUG_PORT, 0, 0, HDHOMERUN_DEBUG_CONNECT_TIMEOUT); } catch(Exception e) { close_internal(); return false; } if(!mDbg.sock.isValid()){ close_internal(); return false; } } try { if (!mDbg.sock.send(message.buffer.getBytes("UTF8"), 0, message.buffer.length(), HDHOMERUN_DEBUG_SEND_TIMEOUT)) { close_internal(); return false; } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block return false; } return true; } private boolean output_message(hdhomerun_debug_message_t message) { mDbg.send_lock.lock(); boolean ret; if (null != mDbg.file_name) { ret = output_message_file(message); } else { ret = output_message_sock(message); } mDbg.send_lock.unlock(); return ret; } private void pop_and_free_message() { mDbg.queue_lock.lock(); hdhomerun_debug_message_t message = mDbg.queue_tail; mDbg.queue_tail = message.prev; if (message.prev != null) { message.prev.next = null; } else { mDbg.queue_head = null; } mDbg.queue_depth--; mDbg.queue_lock.unlock(); message = null; } private class DebugThread extends Thread { public void run() { while (!mDbg.terminate) { mDbg.queue_lock.lock(); hdhomerun_debug_message_t message = mDbg.queue_tail; int queue_depth = mDbg.queue_depth; mDbg.queue_lock.unlock(); if (null == message) { HDHomerun_OS.msleep_approx(250); continue; } if (queue_depth > 1024) { pop_and_free_message(); continue; } if (!output_message(message)) { HDHomerun_OS.msleep_approx(250); continue; } pop_and_free_message(); } } } }