package com.sissi.server.exchange.impl;
import java.io.Closeable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
import com.sissi.commons.Trace;
import com.sissi.commons.apache.IOUtil;
import com.sissi.config.Dictionary;
import com.sissi.config.MongoConfig;
import com.sissi.config.impl.MongoUtils;
import com.sissi.pipeline.Transfer;
import com.sissi.pipeline.TransferBuffer;
import com.sissi.resource.ResourceCounter;
import com.sissi.server.exchange.Exchanger;
import com.sissi.server.exchange.ExchangerContext;
import com.sissi.server.exchange.Terminal;
import com.sissi.thread.Runner;
import com.sissi.thread.impl.ExecuteInterval;
/**
* 索引策略: {"host":1},{"date":1}
*
* @author kim 2013年12月22日
*/
public class BridgeExchangerContext implements ExchangerContext {
private final String date = "date";
private final Log log = LogFactory.getLog(this.getClass());
/**
* {"host":1}
*/
private final DBObject filter = BasicDBObjectBuilder.start(Dictionary.FIELD_HOST, 1).get();
private final Map<String, Exchanger> exchangers = new ConcurrentHashMap<String, Exchanger>();
private final ResourceCounter resourceCounter;
private final MongoConfig config;
private final ExecuteInterval interval;
private final long timeout;
public BridgeExchangerContext(long timeout, Runner runner, ExecuteInterval interval, MongoConfig config, ResourceCounter resourceCounter) {
super();
this.timeout = timeout;
this.interval = interval;
this.config = config.reset();
this.resourceCounter = resourceCounter;
runner.executor(1, new Timeout());
}
@Override
public Exchanger wait(String host, boolean cascade, Transfer transfer) {
BridgeExchanger exchanger = new BridgeExchanger(host, cascade, transfer);
if (MongoUtils.success(this.config.collection().save(this.build(host, true), WriteConcern.SAFE))) {
this.exchangers.put(host, exchanger);
}
return exchanger;
}
@Override
public Exchanger activate(String host) {
if (MongoUtils.success(this.config.collection().remove(this.build(host, false), WriteConcern.SAFE))) {
// Double check 4 multi thread
Exchanger exchanger = this.exchangers.remove(host);
return exchanger != null ? exchanger : new NothingExchanger(host);
}
return new NothingExchanger(host);
}
@Override
public boolean exists(String host) {
return this.config.collection().findOne(this.build(host, false)) != null;
}
/**
* {"host":host,"date":Xxx}
*
* @param host
* @param date
* @return
*/
private DBObject build(String host, boolean date) {
BasicDBObjectBuilder builder = BasicDBObjectBuilder.start(Dictionary.FIELD_HOST, host);
if (date) {
builder.add(this.date, System.currentTimeMillis());
}
return builder.get();
}
/**
* 终止发起方在超时时间内尚未激活的Exchanger
*
* @author kim 2014年4月20日
*/
private class Timeout implements Runnable {
private final long sleep = BridgeExchangerContext.this.interval.convert(TimeUnit.MILLISECONDS);
private final String resource = Timeout.class.getSimpleName();
@Override
public void run() {
try {
BridgeExchangerContext.this.resourceCounter.increment(this.resource);
while (true) {
try {
this.timeout();
Thread.sleep(this.sleep);
} catch (Exception e) {
log.error(e.toString());
Trace.trace(log, e);
}
}
} finally {
BridgeExchangerContext.this.resourceCounter.decrement(this.resource);
}
}
private Timeout timeout() {
try (DBCursor cursor = BridgeExchangerContext.this.config.collection().find(BasicDBObjectBuilder.start(BridgeExchangerContext.this.date, BasicDBObjectBuilder.start("$lt", System.currentTimeMillis() - BridgeExchangerContext.this.timeout).get()).get(), BridgeExchangerContext.this.filter)) {
while (cursor.hasNext()) {
Exchanger exchanger = BridgeExchangerContext.this.activate(MongoUtils.asString(DBObject.class.cast(cursor.next()), Dictionary.FIELD_HOST));
// Double check 4 multi thread
if (exchanger != null) {
BridgeExchangerContext.this.log.warn("Timeout socks: " + exchanger.host());
exchanger.close(Terminal.ALL);
}
}
}
return this;
}
}
private class BridgeExchanger implements Exchanger {
private final Transfer target;
/**
* 是否允许关闭Target
*/
private final boolean cascade;
private final String host;
private Closeable source;
/**
* @param host
* @param cascade 是否允许关闭Target
* @param target 接收方
*/
private BridgeExchanger(String host, boolean cascade, Transfer target) {
this.host = host;
this.cascade = cascade;
this.target = target;
}
public String host() {
return this.host;
}
public BridgeExchanger source(Closeable source) {
this.source = source;
return this;
}
@Override
public BridgeExchanger write(TransferBuffer buffer) {
this.target.transfer(buffer);
return this;
}
@Override
public BridgeExchanger close(Terminal terminal) {
switch (terminal) {
case ALL:
return this.close(Terminal.SOURCE).close(Terminal.TARGET);
case SOURCE:
return this.close(this.source);
case TARGET:
return this.cascade ? this.close(this.target) : this;
}
return this;
}
/**
* 无论是否成功都将隐式激活(Activate)
*
* @param closer
* @return
*/
private BridgeExchanger close(Closeable closer) {
try {
IOUtil.closeQuietly(closer);
} catch (Exception e) {
BridgeExchangerContext.this.log.warn(e.toString());
Trace.trace(BridgeExchangerContext.this.log, e);
} finally {
BridgeExchangerContext.this.activate(this.host);
}
return this;
}
}
private final class NothingExchanger implements Exchanger {
private final String host;
public NothingExchanger(String host) {
super();
this.host = host;
}
@Override
public String host() {
return this.host;
}
@Override
public NothingExchanger source(Closeable source) {
return this;
}
@Override
public NothingExchanger write(TransferBuffer buffer) {
BridgeExchangerContext.this.log.info("Nothing on " + this.host + ", please check");
return this;
}
@Override
public NothingExchanger close(Terminal terminal) {
return this;
}
}
}