public class SocketControl extends AbstractBatch { /** キー添付情報送信バッファキー定数 */ public static final String KEY_SEND_BUFFER = "SEND_BUFFER"; /** キー添付情報受信バッファキー定数 */ public static final String KEY_RECV_BUFFER = "RECV_BUFFER"; /** キー添付情報タイマー情報キー定数 */ public static final String KEY_TIMER = "TIMER"; /** 起動パラメータ:コンフィグファイルパス */ private static final String PARAM_CONFIG_FILE_PATH = "file.config"; /** アプリケーション識別子 */ private static final String APL_CODE = "0101"; /** 監視ログ識別子:情報 */ private static final String WATCH_LOG_INFO = "I"; /** 監視ログ識別子:警告 */ private static final String WATCH_LOG_WARN = "W"; /** 監視ログ識別子:異常 */ private static final String WATCH_LOG_FATAL = "F"; /** 初期受信バッファサイズ */ private static final int INITIAL_BUFFER_SIZE = 8192; /** 動作モード:サーバ */ private static final String MODE_SERVER = "server"; /** 動作モード:クライアント */ private static final String MODE_CLIENT = "client"; /** データ形式:バイナリ形式定義 */ private static final String TYPE_BIN = "bin"; /** データ形式:2進化10進法形式定義 */ private static final String TYPE_BCD = "bcd"; /** データ形式:ASCII形式定義 */ private static final String TYPE_ASC = "asc"; /** T1タイマー名 */ private static final String TIMER_T1 = "T1"; /** T2タイマー名 */ private static final String TIMER_T2 = "T2"; /** TCタイマー名 */ private static final String TIMER_TC = "TC"; /** イベント:接続 */ private static final String EVENT_CONNECT = "#CONNECT"; /** イベント:接続完了 */ private static final String EVENT_ACCEPT = "#ACCEPT"; /** イベント:切断 */ private static final String EVENT_CLOSE = "#CLOSE"; /** データ受信状態:切断 */ private static final int RECV_STAT_DISCONNECT = -1; /** データ受信状態:1データ受信中 */ private static final int RECV_STAT_BUSY = 0; /** データ受信状態:1データ受信済 */ private static final int RECV_STAT_COMPLETE = 1; /** size要素名定義 */ private static final String ELEMENT_SIZE = "size"; /** type要素名定義 */ private static final String ELEMENT_TYPE = "type"; /** 動作モードパラメータ名定数 */ private static final String PARAM_MODE = "mode"; /** 接続先ホスト名パラメータ名定数 */ private static final String PARAM_HOST = "host"; /** 接続ポート番号パラメータ名定数 */ private static final String PARAM_PORT = "port"; /** T1タイマー値パラメータ名定数 */ private static final String PARAM_T1 = TIMER_T1; /** T2タイマー値パラメータ名定数 */ private static final String PARAM_T2 = TIMER_T2; /** TCタイマー値パラメータ名定数 */ private static final String PARAM_TC = TIMER_TC; /** タイマーリトライ回数パラメータ名プレフィックス定数 */ private static final String PARAM_RETRY = "retryFor"; /** ヘッダー部サイズパラメータ名定数 */ private static final String PARAM_HEADER_SIZE = "headerSize"; /** SO_KEEPALIVEに対応するパラメータ名定数 */ private static final String PARAM_KEEP_ALIVE = "keepAlive"; /** TCP_NODELAYに対応するパラメータ名定数 */ private static final String PARAM_TCP_NO_DELAY = "tcpNoDelay"; /** SO_RCVBUFに対応するパラメータ名定数 */ private static final String PARAM_RECEIVE_BUFFER_SIZE = "receiveBufferSize"; /** SO_SNDBUFに対応するパラメータ名定数 */ private static final String PARAM_SEND_BUFFER_SIZE = "sendBufferSize"; /** ウィンドウサイズパラメータ名定数 */ private static final String PARAM_WINDOWS_SIZE = "windowSize"; /** データ種別パラメータ名定数 */ private static final String PARAM_TYPE = "type"; /** 先頭からのデータ位置パラメータ名定数 */ private static final String PARAM_OFFSET = "offset"; /** データサイズパラメータ名定数 */ private static final String PARAM_SIZE = "size"; /** ヘッダー部サイズの含有有無パラメータ名定数 */ private static final String PARAM_CONTAIN = "contain"; /** セミコロン */ private static final String COLON = ":"; /** タイマー監視フラグ - 開始 */ private static final int TIMEOUT_STAT_BEGIN = 0; /** タイマー監視フラグ - タイムアウト発生 */ private static final int TIMEOUT_STAT_OUT = 1; /** ロギングインスタンス */ private static Log log = LogFactory.getLog(SocketControl.class); /** 監視ログ */ protected static WatchStorageLog watchLog = new WatchStorageLog(); /** トレースインスタンス */ private static SocketTrace trace = new SocketTrace(); /** セレクター */ private Selector selector; /** バインド用ソケット */ private ServerSocket serverSocket; /** 終了フラグ */ private boolean exitFlg; /** 設定情報管理インスタンス */ private SocketConfig config; /** 1電文格納用バッファ */ private ByteBuffer receivedBuffer; /** タイマーキュー */ private Map timerQueue; /** * コンストラクタ<br> */ public SocketControl() { } /** * コンストラクタ<br> * @param args 起動パラメータ */ public SocketControl(String[] args) { super(args); } /** * メイン<br> * @param args 起動パラメータ */ public static void main(String[] args) { String methodName = "main()"; log.info(methodName + " Start"); new SocketControl(args); log.info(methodName + " End"); } /** * アプリケーション識別子取得<br> * @return アプリケーション識別子 */ public String getAplCode() { return APL_CODE; } /** * 実処理<br> * @param args 起動パラメータ */ public void execute(String[] args) throws Throwable { String methodName = "execute()"; log.info(methodName + " Start"); // 終了フラグを初期化 exitFlg = false; // タイマー用キューを初期化 timerQueue = new HashMap(); // 起動パラメータを取得 //String file = System.getProperty(PARAM_CONFIG_FILE_PATH); String file = "SocketConfig.xml"; // スタート処理を起動 start(file); log.info(methodName + " End"); } /** * 終了処理<br> * @param args 起動パラメータ */ public void term(String[] args) throws Throwable { String methodName = "term()"; log.info(methodName + " Start"); synchronized (this) { // 終了フラグを初期化 exitFlg = true; } if (selector != null) { // ブロッキング解除 selector.wakeup(); } log.info(methodName + " End"); } /** * スタート処理<br> * @param file コンフィグファイルパス */ private void start(String file) throws Throwable { String methodName = "start()"; log.info(methodName + " Start"); // 設定情報管理のインスタンスを生成 config = new SocketConfig(); // コンフィグファイルを読み込み config.readConfig(file); // 動作モードを取得 String mode = config.getInitialForString(PARAM_MODE); // 動作モードにより処理を振り分け if (mode.equals(MODE_SERVER)) { // サーバモードで動作 doServer(); } else if (mode.equals(MODE_CLIENT)) { // クライアントモードで動作 doClient(); } else { // 動作モード不正 watchLog.info(APL_CODE + WATCH_LOG_WARN + "0001"); } log.info(methodName + " End"); } /** * サーバ処理<br> */ private void doServer() { String methodName = "doServer()"; log.info(methodName + " Start"); try { // 無限ループ while (true) { // サーバ初期化処理 initialServer(); // イベント処理 doEvent(); } } catch (ProcessExitException pee) { log.fatal("service stop"); } catch (Exception e) { log.fatal("server error", e); watchLog.info(APL_CODE + WATCH_LOG_FATAL + "0001"); } catch (Throwable t) { log.fatal("server error", t); watchLog.info(APL_CODE + WATCH_LOG_FATAL + "0001"); } finally { log.info(methodName + " End"); } } /** * クライアント処理<br> */ private void doClient() { String methodName = "doClient()"; log.info(methodName + " Start"); try { // 無限ループ while (true) { // クライアント初期化処理 initialClient(); // イベント処理 doEvent(); } } catch (ProcessExitException pee) { log.fatal("client error", pee); watchLog.info(APL_CODE + WATCH_LOG_FATAL + "0002"); } catch (Exception e) { log.fatal("client error", e); watchLog.info(APL_CODE + WATCH_LOG_FATAL + "0001"); } catch (Throwable t) { log.fatal("client error", t); watchLog.info(APL_CODE + WATCH_LOG_FATAL + "0001"); } finally { log.info(methodName + " End"); } } /** * サーバ初期化処理<br> * @throws IOException 入出力例外 */ private void initialServer() throws IOException { String methodName = "initialServer()"; log.info(methodName + " Start"); try { // 内部変数を初期化 initial(); // 各種インスタンスを生成 selector = SelectorProvider.provider().openSelector(); ServerSocketChannel serverSocketChannel = SelectorProvider.provider().openServerSocketChannel(); // Non-Blockingモードに設定 serverSocketChannel.configureBlocking(false); // 接続元ホスト/接続先ポート番号を取得 String host = config.getInitialForString(PARAM_HOST); int port = config.getInitialForInt(PARAM_PORT); // bindを実行 InetSocketAddress address = new InetSocketAddress(host, port); serverSocket = serverSocketChannel.socket(); serverSocket.bind(address); // セレクターへ登録 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } catch (IOException ioe) { throw ioe; } finally { log.info(methodName + " End"); } } /** * クライアント初期化処理<br> * @throws IOException 入出力例外 */ private void initialClient() throws IOException { String methodName = "initialClient()"; log.info(methodName + " Start"); try { // 内部変数を初期化 initial(); // 各種インスタンスを生成 selector = SelectorProvider.provider().openSelector(); SocketChannel socketChannel = SelectorProvider.provider().openSocketChannel(); // Non-Blockingモードに設定 socketChannel.configureBlocking(false); // 接続先ホスト/接続先ポート番号を取得 String host = config.getInitialForString(PARAM_HOST); int port = config.getInitialForInt(PARAM_PORT); // connectを実行 InetSocketAddress address = new InetSocketAddress(host, port); socketChannel.connect(address); // セレクターへ登録 socketChannel.register(selector, SelectionKey.OP_CONNECT); } catch (IOException ioe) { throw ioe; } finally { log.info(methodName + " End"); } } /** * イベント処理<br> * @throws ProcessExitException プロセス終了例外 * @throws IOException 入出力例外 * @throws Exception 例外 */ private void doEvent() throws ProcessExitException, IOException, Exception { String methodName = "doEvent()"; log.info(methodName + " Start"); try { // TCタイマータイムアウト値を取得 int timeout = config.getInitialForInt(TIMER_TC); // 無限ループ while (true) { // セレクト int keyCount = selector.select(); // 終了フラグをチェック synchronized (this) { if (exitFlg) { // セレクトされたKeysオブジェクトを取得 Iterator keyIterator = selector.keys().iterator(); while (keyIterator.hasNext()) { // キーセットを取得 SelectionKey key = (SelectionKey)keyIterator.next(); // キーセットの種別を判定 log.info("key.interestOps = " + key.interestOps()); if (key.interestOps() != SelectionKey.OP_ACCEPT) { // コネクション切断 doClose(key); } } throw new ProcessExitException(); } } // キー数を判定 if (0 < keyCount) { // セレクトされたSelectionKeyオブジェクトを取得 Iterator keyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) { // キーセットを取得 SelectionKey key = (SelectionKey)keyIterator.next(); // キーセットを削除 keyIterator.remove(); // キーセットの状態により処理を分岐 if (key.isAcceptable()) { // ソケット受付処理 SelectionKey acceptKey = doAccept(key); // シーケンス処理 doSequence(acceptKey, EVENT_ACCEPT, null); } else if (key.isConnectable()) { // ソケット接続処理 boolean result = doFinishConnect(key); if (result) { // シーケンス処理 doSequence(key, EVENT_CONNECT, null); } else { // 待機 sleep(timeout); // 再接続処理 doConnect(); } } else if (key.isReadable()) { // データ読み込み処理 int recvStat = doRead(key); // 1データ受信済 if (recvStat == RECV_STAT_COMPLETE) { // 電文種別を取得 String telegramID = getTelegramID(receivedBuffer); // シーケンス処理 doSequence(key, telegramID, receivedBuffer); // 電文格納用バッファをクリア receivedBuffer = null; } // 切断 else if (recvStat == RECV_STAT_DISCONNECT) { // シーケンス処理 doSequence(key, EVENT_CLOSE, null); } } else if (key.isWritable()) { // ソケット受付処理 doWrite(key); } } } else { // セレクトされたKeysオブジェクトを取得 Iterator keyIterator = selector.keys().iterator(); while (keyIterator.hasNext()) { // キーセットを取得 SelectionKey key = (SelectionKey)keyIterator.next(); // キーセットの種別を判定 if (key.interestOps() != SelectionKey.OP_READ) continue; Map map = (Map) key.attachment(); SocketTimerInfo socketTimerInfo = (SocketTimerInfo) map.get(KEY_TIMER); String timerType = socketTimerInfo.getTimerType(); int flag = socketTimerInfo.getFlag(); if (flag != TIMEOUT_STAT_OUT) continue; socketTimerInfo.setFlag(TIMEOUT_STAT_BEGIN); // タイムアウト回数を取得 int timeoutCount = getTimeoutCount(socketTimerInfo); timeoutCount++; // リトライ回数を取得 int retryCount = config.getInitialForInt(PARAM_RETRY + timerType); // リトライアウトを判定 if (timeoutCount >= retryCount) { log.warn(timerType + " retry out"); // タイマーキューから削除 timerQueue.remove(socketTimerInfo); // シーケンス処理 doSequence(key, timerType, null); } else { log.warn(timerType + " timeout : " + String.valueOf(timeoutCount)); // タイマー再開始 startTimer(key, socketTimerInfo, timeoutCount); } break; } } } } catch (ProcessExitException pee) { serverSocket.close(); throw pee; } catch (IOException ioe) { log.fatal("event error", ioe); watchLog.info(APL_CODE + WATCH_LOG_FATAL + "0001"); serverSocket.close(); } catch (Exception e) { log.fatal("event error", e); // watchLog.info(APL_CODE + WATCH_LOG_FATAL + "0003"); serverSocket.close(); } finally { log.info(methodName + " End"); } } /** * ソケット接続処理<br> * @throws IOException 入出力例外 */ private void doConnect() throws IOException { String methodName = "doConnect()"; log.info(methodName + " Start"); try { // 内部変数を初期化 initial(); // 各種インスタンスを生成 selector = SelectorProvider.provider().openSelector(); SocketChannel socketChannel = SelectorProvider.provider().openSocketChannel(); // Non-Blockingモードに設定 socketChannel.configureBlocking(false); // 接続先ホスト/接続先ポート番号を取得 String host = config.getInitialForString(PARAM_HOST); int port = config.getInitialForInt(PARAM_PORT); // connectを実行 InetSocketAddress address = new InetSocketAddress(host, port); socketChannel.connect(address); // セレクターへ登録 socketChannel.register(selector, SelectionKey.OP_CONNECT); } catch (IOException ioe) { throw ioe; } finally { log.info(methodName + " End"); } } /** * ソケット接続完了処理<br> * @param key キーセット * @return 処理結果 true:正常 false:異常 * @throws IOException 入出力例外 */ private boolean doFinishConnect(SelectionKey key) throws IOException { String methodName = "doFinishConnect()"; log.info(methodName + " Start"); boolean result = false; SocketChannel socketChannel = null; try { socketChannel = (SocketChannel)key.channel(); if (socketChannel.isConnectionPending()) { // コネクションを確立 socketChannel.finishConnect(); watchLog.info(APL_CODE + WATCH_LOG_INFO + "0001"); // ソケットパラメータを取得 boolean keepAlive = config.getInitialForBoolean(PARAM_KEEP_ALIVE); int receiveBufferSize = config.getInitialForInt(PARAM_RECEIVE_BUFFER_SIZE); int sendBufferSize = config.getInitialForInt(PARAM_SEND_BUFFER_SIZE); boolean tcpNoDelay = config.getInitialForBoolean(PARAM_TCP_NO_DELAY); // ソケットパラメータを設定 socketChannel.socket().setKeepAlive(keepAlive); socketChannel.socket().setReceiveBufferSize(receiveBufferSize); socketChannel.socket().setSendBufferSize(sendBufferSize); socketChannel.socket().setTcpNoDelay(tcpNoDelay); // 接続完了先IPアドレスを内部ログに出力 log.info("finishConnect remote address = " + socketChannel.socket().getRemoteSocketAddress()); // キーセットにキーを追加 key.interestOps(SelectionKey.OP_READ); result = true; } } catch (IOException ioe) { log.fatal("connect error", ioe); } finally { log.info(methodName + " End"); } return result; } /** * ソケット受付処理<br> * @param key キーセット * @return 接続後キーセット * @throws IOException 入出力例外 */ private SelectionKey doAccept(SelectionKey key) throws IOException ,Exception{ String methodName = "doAccept()"; log.info(methodName + " Start"); SelectionKey acceptKey = null; try { ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel(); // acceptを実行 SocketChannel socketChannel = serverSocketChannel.accept(); // Non-Blocking モードに変更 socketChannel.configureBlocking(false); watchLog.info(APL_CODE + WATCH_LOG_INFO + "0002"); // ソケットパラメータを取得 boolean keepAlive = config.getInitialForBoolean(PARAM_KEEP_ALIVE); int receiveBufferSize = config.getInitialForInt(PARAM_RECEIVE_BUFFER_SIZE); int sendBufferSize = config.getInitialForInt(PARAM_SEND_BUFFER_SIZE); boolean tcpNoDelay = config.getInitialForBoolean(PARAM_TCP_NO_DELAY); // ソケットパラメータを設定 socketChannel.socket().setKeepAlive(keepAlive); socketChannel.socket().setReceiveBufferSize(receiveBufferSize); socketChannel.socket().setSendBufferSize(sendBufferSize); socketChannel.socket().setTcpNoDelay(tcpNoDelay); // 接続先IPアドレスを内部ログに出力 log.info("connect remote address = " + socketChannel.socket().getRemoteSocketAddress()); // セレクターへ登録 acceptKey = socketChannel.register(selector, SelectionKey.OP_READ); } catch (IOException ioe) { throw ioe; } catch (Exception e) { throw e; } finally { log.info(methodName + " End"); } return acceptKey; } /** * データ読み込み処理<br> * @param key キーセット * @return 処理結果 -1:切断 0:1データ受信中 1:1データ受信済 * @throws Exception 例外 */ private int doRead(SelectionKey key) throws Exception { String methodName = "doRead()"; log.info(methodName + " Start"); int recvStat = RECV_STAT_BUSY; try { SocketChannel socketChannel = (SocketChannel)key.channel(); // 受信先IPアドレスを内部ログに出力 log.info("read remote address = " + socketChannel.socket().getRemoteSocketAddress()); int receiveSize = 0; // ヘッダー部サイズを取得 int headerSize = config.getInitialForInt(PARAM_HEADER_SIZE); // 受信バッファを取得 Map map = (Map) key.attachment(); ByteBuffer recvBuffer = (ByteBuffer) map.get(KEY_RECV_BUFFER); // 既に受信済み電文が存在するかチェック if (recvBuffer != null) { // 受信済み電文が存在する if (recvBuffer.limit() >= headerSize) { // ヘッダー部サイズ以上の電文を受信済み int telegramSize = getTelegramSize(recvBuffer); receiveSize = telegramSize - recvBuffer.limit(); } else { // ヘッダー部サイズ以上の電文は未受信 receiveSize = headerSize - recvBuffer.limit(); } } else { // 受信済み電文が存在しない receiveSize = headerSize; } while (true) { // readを実行 log.info("receiveSize = " + receiveSize); ByteBuffer receiveData = ByteBuffer.allocateDirect(receiveSize); receiveData.clear(); int receivedSize = socketChannel.read(receiveData); log.info("receivedSize = " + receivedSize); if (receivedSize < 0) { // watchLog.info(APL_CODE + WATCH_LOG_WARN + "0002"); recvStat = RECV_STAT_DISCONNECT; break; } else { // 受信済み電文に受信電文をコピー if (recvBuffer != null) { recvBuffer = appendByteBuffer(recvBuffer, receiveData, receivedSize); } else { recvBuffer = copyByteBuffer(receiveData, receivedSize); } // 受信電文が指定サイズ分受信したかチェック if (receivedSize >= receiveSize) { // 指定サイズ分受信済み int telegramSize = getTelegramSize(recvBuffer); if (telegramSize <= recvBuffer.limit()) { // 1電文を受信 receivedBuffer = ByteBuffer.allocateDirect(telegramSize); receivedBuffer.clear(); receivedBuffer.put(recvBuffer); receivedBuffer.rewind(); // 受信済み電文をクリア recvBuffer = null; map.put(KEY_RECV_BUFFER, null); // トレースを出力 trace.trace(SocketTrace.RECV, receivedBuffer); recvStat = RECV_STAT_COMPLETE; break; } else { // ヘッダー部を受信 receiveSize = telegramSize - recvBuffer.limit(); } } else { // 受信バッファをマップに設定 map.put(KEY_RECV_BUFFER, recvBuffer); break; } } } } catch (IOException ioe) { watchLog.info(APL_CODE + WATCH_LOG_WARN + "0002"); recvStat = RECV_STAT_DISCONNECT; } finally { log.info(methodName + " End"); } return recvStat; } /** * データ書き込み処理<br> * @param key キーセット * @throws Exception 例外 */ private void doWrite(SelectionKey key) throws Exception { String methodName = "doWrite()"; log.info(methodName + " Start"); try { SocketChannel socketChannel = (SocketChannel)key.channel(); Map map = (Map) key.attachment(); ByteBuffer buff = (ByteBuffer) map.get(KEY_SEND_BUFFER); // 応答先IPアドレスを内部ログに出力 log.info("write remote address = " + socketChannel.socket().getRemoteSocketAddress()); // トレース出力 trace.trace(SocketTrace.SEND, buff); // writeを実行 buff.rewind(); socketChannel.write(buff); // 送信データ用バッファをクリア buff = null; // キーセットにキーを追加 key.interestOps(SelectionKey.OP_READ); } catch (IOException ioe) { throw ioe; } finally { log.info(methodName + " End"); } } /** * ソケット切断処理<br> * @param key キーセット * @throws IOException 入出力例外 */ private void doClose(SelectionKey key) throws IOException { String methodName = "doClose()"; log.info(methodName + " Start"); try { SocketChannel socketChannel = (SocketChannel)key.channel(); // 切断要求IPアドレスを内部ログに出力 log.info("close remote address = " + socketChannel.socket().getRemoteSocketAddress()); // リソース解放 Map map = (Map) key.attachment(); map = null; // ソケットクローズ socketChannel.close(); } catch (IOException ioe) { throw ioe; } finally { log.info(methodName + " End"); } } /** * シーケンス処理<br> * @param key キーセット * @param event イベント種別 * @param data 受信データ * @throws Exception 例外 */ private void doSequence( SelectionKey key, String event, ByteBuffer data) throws Exception { String methodName = "doSequence()"; log.info(methodName + " Start"); try { // シナリオ数を取得 int turnCount = config.getTurnCount(event); for (int i = 0 ; i < turnCount; i++) { // シナリオ情報を取得 SocketTurn turn = config.getTurn(event, i); String action = turn.getAction(); String value = turn.getValue(); // クラス実行の場合 if (action.equals(SocketTurn.ACTION_RUN)) { // 指定クラスのインスタンスを生成 Class runClass = Class.forName(value); SocketAddon runInstance = (SocketAddon)runClass.newInstance(); // パラメータを設定 int size = 0; if (receivedBuffer != null) { size = getTelegramSize(receivedBuffer); } SocketParameter socketParam = new SocketParameter(); socketParam.setDataType(event); socketParam.setDataSize(size); socketParam.setData(data); // 呼び出し SocketAction socketAction = runInstance.execute(socketParam); // データ送信の場合 if (socketAction.getAction() == SocketAction.SEND) { // 送信データを設定 ByteBuffer buff = ByteBuffer.allocateDirect(socketAction.getData().limit()); buff.put(socketAction.getData()); buff.rewind(); Map map = (Map) key.attachment(); if (map == null) { map = new HashMap(); } map.put(KEY_SEND_BUFFER, buff); // キーセットにキーを追加 key.interestOps(SelectionKey.OP_WRITE); } // 処理スキップの場合 else if (socketAction.getAction() == SocketAction.SKIP) { // シーケンス処理をスキップ break; } // 処理なしの場合 else { // 処理なし } } // タイマー開始の場合 else if (action.equals(SocketTurn.ACTION_START_TIMER)) { SocketTimerInfo socketTimerInfo = new SocketTimerInfo(); socketTimerInfo.setTimerType(value); socketTimerInfo.setTimerKey(key.toString()); socketTimerInfo.setFlag(TIMEOUT_STAT_BEGIN); startTimer(key, socketTimerInfo, 0); } // タイマー停止の場合 else if (action.equals(SocketTurn.ACTION_STOP_TIMER)) { Map map = (Map) key.attachment(); SocketTimerInfo socketTimerInfo = (SocketTimerInfo) map.get(KEY_TIMER); socketTimerInfo.setFlag(TIMEOUT_STAT_BEGIN); stopTimer(socketTimerInfo); } // コネクション接続の場合 else if (action.equals(SocketTurn.ACTION_CONNECT)) { doConnect(); } // コネクション切断の場合 else if (action.equals(SocketTurn.ACTION_CLOSE)) { doClose(key); } // 処理なしの場合 else if (action.equals(SocketTurn.ACTION_NOP)) { } // 上記以外の場合 else { watchLog.info(APL_CODE + WATCH_LOG_WARN + "0003"); } } } catch (Exception e) { log.fatal("sequence error", e); doClose(key); } finally { log.info(methodName + " End"); } } /** * 内部変数初期化<br> */ private void initial() { receivedBuffer = null; } /** * タイマー開始 * @param key キーセット * @param name タイマー名 * @param count タイムアウト回数 */ private void startTimer(SelectionKey key, SocketTimerInfo socketTimerInfo, int count) { String timerType = socketTimerInfo.getTimerType(); // タイマー名を設定 Map map = (Map) key.attachment(); if (map == null) { map = new HashMap(); } map.put(KEY_TIMER, socketTimerInfo); key.attach(map); // タイムアウト値を取得 int timeout = config.getInitialForInt(timerType); // タイマーを生成 SocketTimer timer = new SocketTimer(key, timeout); // タイマー情報クラスを設定 TimerInfo info = new TimerInfo(timer, count); // タイマーキューに設定 timerQueue.put(socketTimerInfo, info); // タイマーを開始 timer.start(); // 内部ログにタイマー開始を出力 log.info(timerType + " timer start"); } /** * タイマー停止<br> * @param name 停止するタイマー名 null:全タイマーを停止 */ private void stopTimer(SocketTimerInfo socketTimerInfo) { String name = socketTimerInfo.getTimerType(); if (name == null) { // キューの全タイマーを停止 Iterator iterator = timerQueue.keySet().iterator(); while (iterator.hasNext()) { String key = (String)iterator.next(); TimerInfo info = (TimerInfo)timerQueue.get(key); timerQueue.remove(key); SocketTimer timer = info.getTimer(); timer.stopTimer(); timer = null; // 内部ログにタイマー停止を出力 log.info(name + " timer stopped"); } } else { // キューの該当タイマーのみを停止 if (timerQueue.containsKey(socketTimerInfo)) { TimerInfo info = (TimerInfo)timerQueue.get(socketTimerInfo); timerQueue.remove(socketTimerInfo); SocketTimer timer = info.getTimer(); timer.stopTimer(); timer = null; // 内部ログにタイマー停止を出力 log.info(name.split(COLON)[0] + " timer stopped"); } } } /** * タイムアウト回数取得<br> * @param name タイマー名 * @return タイムアウト回数 */ private int getTimeoutCount(SocketTimerInfo socketTimerInfo) { int count = 0; // キューから該当タイマーを取得 if (timerQueue.containsKey(socketTimerInfo)) { TimerInfo info = (TimerInfo)timerQueue.get(socketTimerInfo); timerQueue.remove(socketTimerInfo); // タイムアウト回数を取得 count = info.getTimeoutCount(); } return count; } /** * リソース解放処理<br> * @param key キーセット * @throws IOException 入出力例外 */ private void releaseResource(SelectionKey key) throws IOException { String methodName = "releaseResource()"; log.info(methodName + " Start"); try { // 全タイマーを停止 stopTimer(null); // ソケットを切断 doClose(key); // 内部変数を初期化 initial(); } catch (IOException ioe) { throw ioe; } finally { log.info(methodName + " End"); } } /** * 電文長取得<br> * @param header ヘッダーデータ * @return 電文長(ヘッダー部含む) */ private int getTelegramSize(ByteBuffer header) { // 設定情報を取得 int headerSize = config.getInitialForInt(PARAM_HEADER_SIZE); String type = config.getHeaderForString(ELEMENT_SIZE, PARAM_TYPE); int offset = config.getHeaderForInt(ELEMENT_SIZE, PARAM_OFFSET); int size = config.getHeaderForInt(ELEMENT_SIZE, PARAM_SIZE); boolean contain = config.getHeaderForBoolean(ELEMENT_SIZE, PARAM_CONTAIN); byte[] dst = new byte[size]; header.rewind(); header.position(offset); header.get(dst); header.rewind(); int dataSize = 0; if (type.equals(TYPE_BIN)) { // バイナリ形式 //dataSize = dst[0] * 0x100 + (dst[1] & 0xFF); //dataSize = dst[0] * 0x100 + (dst[1] & 0xFF) + (dst[2] & 0xFF) + (dst[3] & 0xFF); for (int k = 0; k < dst.length; k++) { dataSize = (dataSize << 8) | (dst[k] & 0xFF); } } else { // その他(ASCII)形式 dataSize = Integer.parseInt(new String(dst)); } if (!contain) { // 電文長にヘッダー部サイズが含まれていない場合 dataSize += headerSize; } return dataSize; } /** * 電文種別取得<br> * @param header ヘッダーデータ * @return 電文種別 */ private String getTelegramID(ByteBuffer header) { // 設定情報を取得 String type = config.getHeaderForString(ELEMENT_TYPE, PARAM_TYPE); int offset = config.getHeaderForInt(ELEMENT_TYPE, PARAM_OFFSET); int size = config.getHeaderForInt(ELEMENT_TYPE, PARAM_SIZE); byte[] dst = new byte[size]; header.rewind(); header.position(offset); header.get(dst); header.rewind(); String dataType; if (type.equals(TYPE_BIN)) { // バイナリ形式 dataType = Long.toHexString(dst[0] * 0x100 + (dst[1] & 0xFF)); } else { // その他(ASCII)形式 dataType = new String(dst); } return dataType; } /** * ByteBufferコピー<br> * @param src コピー元ByteBuffer * @param size コピーサイズ * @return コピー後ByteBuffer */ private ByteBuffer copyByteBuffer(ByteBuffer src, int size) { byte[] temp = new byte[size]; src.position(0); src.get(temp); ByteBuffer dst = ByteBuffer.allocateDirect(temp.length); dst.clear(); dst.put(temp); dst.flip(); return dst; } /** * ByteBuffer連結<br> * @param buff1 ByteBuffer1 * @param buff2 ByteBuffer2 * @param size 連結サイズ * @return 連結後ByteBuffer */ private ByteBuffer appendByteBuffer(ByteBuffer buff1, ByteBuffer buff2, int size) { byte[] temp1 = new byte[buff1.limit()]; byte[] temp2 = new byte[size]; buff1.position(0); buff1.get(temp1); buff2.position(0); buff2.get(temp2); ByteBuffer dst = ByteBuffer.allocateDirect(temp1.length + temp2.length); dst.clear(); dst.put(temp1); dst.put(temp2); dst.flip(); return dst; } /** * スリープ<br> * @param timeout スリープ時間(単位:秒) */ private synchronized void sleep(int timeout) { try { wait(timeout * 1000); } catch (InterruptedException e) {} } /** * タイマー情報を管理するクラス。<br> * * @author 福田 剛 * @version 1.0 * */ class TimerInfo { /** タイマースレッド */ SocketTimer timer; /** タイムアウト回数 */ int timeoutCount; /** * コンストラクタ<br> * @param timer タイマークラス * @param count タイムアウト回数 */ public TimerInfo(SocketTimer timer, int count) { this.timer = timer; this.timeoutCount = count; } /** * @return timeoutCount を戻します。 */ public int getTimeoutCount() { return timeoutCount; } /** * @return timer を戻します。 */ public SocketTimer getTimer() { return timer; } } }