All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gu.dtalk.IAckAdapter Maven / Gradle / Ivy

There is a newer version: 0.8.2
Show newest version
package gu.dtalk;

import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Preconditions.checkNotNull;

import gu.dtalk.Ack.Status;
import gu.simplemq.IMessageAdapter;
import gu.simplemq.exceptions.SmqUnsubscribeException;

/**
 * 命令响应{@link Ack}处理接口
* 设备命令发送到设备端,设备端执行命令后会返回命令响应({@link Ack},也就是命令执行结果)给发送端。 * 该接口就是命令发送端用于处理收到的命令响应的接口。
* 应用项目应根据需要实现对应命令响应的业务。建议继承{@link BaseAdapter},因为{@link BaseAdapter}实现了所有的接口方法, * 应用项目只需要重写{@link BaseAdapter#onSubscribe(Ack)}方法就可以了
* 因为设备命令发送功能可以支持同时向多个设备或设备组发送命令,所以一条设备命令发送会收到多个设备端的响应, * 虽然发送方可以知道知道收到命令的设备端数量,但系统并不能保证所有收到命令的设备都会返回命令响应, * 所以{@link BaseAdapter#setDuration(long)}用于指定超时参数,超过指定时间还没有收到所有命令响应, * 本机会产生一个虚拟的{@link Ack}消息,送给{@code IAckAdapter}接口, * 参见{@link BaseAdapter#onSubscribe(Ack)},{@link Ack.Status#TIMEOUT} * @author guyadong * * @param 设备命令响应返回数据类型 */ public interface IAckAdapter extends IMessageAdapter>{ /** 默认超时时间(毫秒) */ public static final long DEFAULT_DURATION = 60000L; /** * 命令响应{@link Ack}处理基类, * 应用项目可以继承此类的方法({@link #doOnTimeout()},{@link #doOnSubscribe(Ack)},{@link #doOnZeroClient()})实现自己的业务逻辑 * @author guyadong * * @param 设备命令响应返回数据类型 */ public static abstract class BaseAdapter implements IAckAdapter { /** 收到命令的client端数量,初始值-1,只有{@link #setClientNum(long)}被调用后才有效 */ private final AtomicLong clientNum = new AtomicLong(-1L); /** * 命令响应等待时间(毫秒), * 超过这个时间会自动取消频道订阅,>0有效,默认为0时无限期。 */ private long duration = 0L; /** 收到的命令响应计数 */ private long ackCount = 0L; /** 当前对象频道订阅生命期是否结束标志 */ private final AtomicBoolean isFinished = new AtomicBoolean(Boolean.FALSE); /** * 默认构造函数
* 有效期使用默认值{@link #DEFAULT_DURATION} */ public BaseAdapter() { this(DEFAULT_DURATION,TimeUnit.MILLISECONDS); } /** * 构造函数 * @param duration 命令响应等待时间 * @param unit 时间单位 */ public BaseAdapter(long duration,TimeUnit unit) { this.setDuration(duration, unit); } /** 处理超时情况,应用项目应重写此方法实现自己的业务逻辑 */ protected void doOnTimeout(){} /** 处理接收命令的设备端数量为0的情况,应用项目应重写此方法实现自己的业务逻辑 */ protected void doOnZeroClient(){} /** * 执行正常响应处理业务逻辑,应用项目应重写此方法实现自己的业务逻辑 * @param t 响应对象 */ protected void doOnSubscribe(Ack t){} /** * @return 返回{@link #clientNum}值 */ protected final long getClientNum(){ return clientNum.get(); } /** * @return 返回{@link #ackCount}值 */ protected final long getAckCount() { return ackCount; } /** 通知等待的线程 */ private final void doOnFinished(){ synchronized(this){ checkState(this.isFinished.compareAndSet(false, true),"invalid status of isFinished"); this.notifyAll(); } } /** * 命令响应处理实现 */ @Override public final void onSubscribe(Ack t) throws SmqUnsubscribeException { if(t.getStatus() == Status.TIMEOUT ){ // 收到这个状态时频道已经被取消订阅 if(clientNum.get() == 0){ doOnZeroClient(); }else{ doOnTimeout(); } doOnFinished(); }else{ try{ doOnSubscribe(t); }finally{ if(++ackCount ==clientNum.get()){ doOnFinished(); // 所有收到命令的设备都已经响应则抛出SmqUnsubscribeException异常用于取消当前频道订阅 throw new SmqUnsubscribeException(true); } } } } /** * 只能被调用一次,否则第二次会抛出异常, * 设备命令发送后,REDIS会返回收到设备命令的设备端数量,{@code clientNum}必须大等于0 */ @Override public BaseAdapter setClientNum(long clientNum){ checkArgument(clientNum >= 0,"INVALID clientNum %s",clientNum); checkState(this.clientNum.compareAndSet(-1L, clientNum),"clientNum can be set once only"); return this; } @Override public final long getDuration() { return duration; } @Override public boolean isFinished() { return this.isFinished.get() || ackCount == this.clientNum.get(); } /** * 设置超时时间(毫秒) * @param duration 大于0有效 * @return 当前对象 */ public final BaseAdapter setDuration(long duration) { this.duration = duration; return this; } /** * 设置超时时间 * @param duration 超时时间 * @param unit 时间单位 * @return 当前对象 */ public final BaseAdapter setDuration(long duration,TimeUnit unit) { return setDuration(TimeUnit.MILLISECONDS.convert(duration, checkNotNull(unit,"unit is null"))); } /** * 设置有效期时间戳(毫秒) * @param expire 效期时间戳(毫秒) * @return 当前对象 */ public final BaseAdapter setExpire(long expire) { return setDuration(expire - System.currentTimeMillis()); } /** * @param expire 效期时间戳 * @return 当前对象 * @see #setExpire(long) */ public final BaseAdapter setExpire(Date expire) { return setDuration(expire.getTime() - System.currentTimeMillis()); } /** * 等待命令响应订阅结束,用于同步接收命令响应 * @throws InterruptedException 中断异常 */ public final void waitFinished() throws InterruptedException{ synchronized(this){ while(!isFinished()){ this.wait(200); } } } /** * 复位所有成员变量到初始状态,以便于对象下次复用 * @return 当前对象 */ public final synchronized BaseAdapter reset(){ this.clientNum.set(-1L); this.duration = 0L; this.ackCount = 0L; this.isFinished.set(false); return this; } } /** * 设置收到命令的client端数量
* 用于判断是否已经收到所有设备的响应命令. * @param clientNum 收到命令的client端数量 * @return 当前对象 */ public IAckAdapter setClientNum(long clientNum); /** * @return 返回命令响应等待持续时间(毫秒) */ public long getDuration(); /** * @return 返回当前响应任务是否结束 */ public boolean isFinished(); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy