/*
 * Paraselene
 * Copyright (c) 2009, 2010  Akira Terasaki
 * このファイルは同梱されているLicense.txtに定めた条件に同意できる場合にのみ
 * 利用可能です。
 */
package paraselene.supervisor;


import java.util.*;

/**
 * タイムアウトによる強制停止発生。<BR>
 * Pageインスタンスのinput()またはoutput()は規定時間(デフォルト3分)リターン
 * しなかった場合、タイムアウトにより強制停止させられます。
 * その際に発生する例外です。<BR>
 * このタイムアウト時間の変更は、{@link SandBox#setTimeoutMinute(long)}
 * にて変更可能です。<br><br>
 * この例外が発生する場合、システム設計の見直しを推奨します。<BR><BR>
 * synchronized中で同期処理を行っている箇所があり、そこで強制停止した場合、
 * 同期を取ろうとしているオブジェクトの更新が不完全で、データ不整合を起こす
 * 可能性があります。<BR>
 * また、クローズしていないリソースが残存する可能性もあります。<BR>
 * そのため、この例外が発生した場合は、サーブレットコンテナの再起動を推奨します。
 */
public class KilledByTimeoutException extends Exception {
	private static final long serialVersionUID = 2L;
	ArrayList<StackTraceElement[]>	snap;
	KilledByTimeoutException( StackTraceElement[] one, ArrayList<StackTraceElement[]> all ){
		super();
		setStackTrace( one );
		snap = all;
	}

	public String toString() {
		StringBuilder	buf = new StringBuilder( super.toString() );
		int	cnt = snap.size();
		for ( int i = 0; i < cnt; i++ ) {
			buf = buf.append( "\n=====[job " );
			buf = buf.append( i );
			buf = buf.append( "]=======================================\n" );
			StackTraceElement[]	s = snap.get( i );
			for ( int j = 0; j < s.length; j++ ) {
				buf = buf.append( s[j].toString() );
				buf = buf.append( "\n" );
			}
			buf = buf.append( "=====\n" );
		}
		return buf.toString();
	}
}

class ChildKiller extends Thread {
	private static ArrayList<Child>	running = null;
	private static ChildKiller	killer = new ChildKiller();

	private ChildKiller(){}

	static void init( ArrayList<Child> list ) {
		synchronized( killer ) {
			if ( running != null )	return;
			running = list;
			killer.setDaemon( true );
			killer.start();
		}
	}

	@SuppressWarnings("deprecation")
	static KilledByTimeoutException kill( Child child ) {
		ArrayList<StackTraceElement[]>	list = new ArrayList<StackTraceElement[]>();
		StackTraceElement[]	crt = null;
		synchronized( running ) {
			int	cnt = running.size();
			for ( int i = 0; i < cnt; i++ ) {
				Child	c = running.get( i );
				if ( c == child ) {
					crt = c.getStackTrace();
					continue;
				}
				list.add( c.getStackTrace() );
			}
		}
		KilledByTimeoutException	ke = new KilledByTimeoutException( crt, list );
		child.kbt = ke;
		try{
			child.stop();
		}
		catch( Throwable e ){}
		synchronized( running ) {
			running.remove( child );
		}
		return ke;
	}

	public void run() {
		while( true ) {
			try {
				Thread.sleep( 1000 * 60 * 5 );
				Child[]	c = null;
				synchronized( running ) {
					c = running.toArray( new Child[0] );
				}
				long	now = new Date().getTime();
				for ( int i = 0; i < c.length; i++ ) {
					if ( c[i].daemon == null )	continue;
					if ( !c[i].isAlive() )	continue;
					if ( c[i].limit_dt.getTime() > now )	continue;
					c[i].kbt = kill( c[i] );
				}
			}
			catch( Throwable e ){}
		}
	}
}

