/* * Copyright 2013 Eediom Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.araqne.logdb.client.http.impl; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeoutException; import org.araqne.logdb.client.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 현재 블록킹 호출 중인 모든 RPC 호출의 목록을 관리합니다. * * @since 0.5.0 * @author xeraph@eediom.com * */ public class WebSocketBlockingTable { private final Logger logger = LoggerFactory.getLogger(WebSocketBlockingTable.class); private Message interruptSignal = new Message(); private ConcurrentMap<String, WaitingCall> lockMap = new ConcurrentHashMap<String, WaitingCall>(); private WebSocketSession session; public WebSocketBlockingTable(WebSocketSession webSocketSession) { this.session = webSocketSession; } public WaitingCall set(String guid) { WaitingCall item = new WaitingCall(guid); lockMap.put(guid, item); return item; } public void signal(String guid, Message response) { if (logger.isDebugEnabled()) logger.debug("araqne-rpc: signal call response {}", guid); WaitingCall item = lockMap.get(guid); if (item == null) { logger.warn("araqne-rpc: no waiting item {}, maybe timeout", guid); return; } synchronized (item) { item.done(response); item.notifyAll(); } } public Message await(WaitingCall item) throws InterruptedException { if (logger.isDebugEnabled()) logger.debug("araqne logdb client: waiting call response id {}", item.getGuid()); try { synchronized (item) { while (item.getResult() == null) item.wait(); } if (item.getResult() == interruptSignal) throw new InterruptedException("call cancelled: " + item.getGuid()); } finally { if (logger.isDebugEnabled()) logger.debug("araqne logdb client: removing blocking lock id {}", item.getGuid()); lockMap.remove(item.getGuid()); } return item.getResult(); } public Message await(WaitingCall item, long timeout) throws InterruptedException, TimeoutException { long before = new Date().getTime(); try { synchronized (item) { while (item.getResult() == null) { item.wait(timeout); if (new Date().getTime() - before >= timeout) { if (logger.isDebugEnabled()) logger.debug("araqne logdb client: blocking timeout of id {}", item.getGuid()); break; } } } if (item.getResult() == interruptSignal) throw new InterruptedException("call cancelled: " + item.getGuid()); else if (item.getResult() != null) return item.getResult(); else throw new TimeoutException("websocket message read timeout"); } finally { if (logger.isDebugEnabled()) logger.debug("araqne logdb client: blocking finished for id {}", item.getGuid()); lockMap.remove(item.getGuid()); } } public void close() { logger.debug("interrupting all blocking calls: " + session.toString()); // cancel all blocking calls for (String guid : lockMap.keySet()) { WaitingCall item = lockMap.get(guid); if (item == null) continue; synchronized (item) { item.done(interruptSignal); item.notifyAll(); } } lockMap.clear(); } }