001package gu.dtalk; 002 003import java.lang.reflect.Type; 004import java.util.Arrays; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.List; 008import java.util.Map; 009 010import com.alibaba.fastjson.annotation.JSONField; 011import com.alibaba.fastjson.parser.ParserConfig; 012import com.alibaba.fastjson.util.TypeUtils; 013import com.google.common.base.Function; 014import com.google.common.base.MoreObjects; 015import com.google.common.base.Throwables; 016import com.google.common.collect.Collections2; 017import com.google.common.collect.Lists; 018import com.google.common.collect.Maps; 019 020import gu.dtalk.exception.CmdExecutionException; 021import static com.google.common.base.Preconditions.*; 022 023/** 024 * 设备命令条目 025 * @author guyadong 026 * 027 */ 028public class CmdItem extends BaseItem { 029 030 private static final Function<BaseItem, BaseOption<Object>> TO_OPTION = new Function<BaseItem,BaseOption<Object>>(){ 031 032 @SuppressWarnings("unchecked") 033 @Override 034 public BaseOption<Object> apply(BaseItem input) { 035 return (BaseOption<Object>) input; 036 }}; 037 private static final Function<BaseOption<?>,BaseItem> TO_ITEM = new Function<BaseOption<?>,BaseItem>(){ 038 039 @Override 040 public BaseItem apply(BaseOption<?> input) { 041 return input; 042 }}; 043 private static final Function<BaseItem, Object> TO_VALUE = new Function<BaseItem, Object>() { 044 @Override 045 public Object apply(BaseItem input) { 046 return ((BaseOption<?>) input).fetch(); 047 } 048 }; 049 @JSONField(serialize = false,deserialize = false) 050 private ICmdAdapter cmdAdapter; 051 052 /** 053 * 任务队列名<br> 054 * 该字段不为空时,对象支持队列任务 055 */ 056 private String taskQueue; 057 public CmdItem() { 058 } 059 060 @Override 061 public final boolean isContainer() { 062 return true; 063 } 064 065 @Override 066 public final ItemType getCatalog() { 067 return ItemType.CMD; 068 } 069 @JSONField(serialize = false,deserialize = false) 070 public List<BaseOption<Object>> getParameters(){ 071 return Lists.transform(getChilds(),TO_OPTION); 072 } 073 @JSONField(serialize = false,deserialize = false) 074 public void setParameters(List<BaseOption<?>> parameters){ 075 items.clear(); 076 addParameters(parameters); 077 } 078 public CmdItem addParameters(BaseOption<?> ... parameter){ 079 return addParameters(Arrays.asList(parameter)); 080 } 081 public CmdItem addParameters(Collection<BaseOption<?>> parameters){ 082 addChilds(Collections2.transform(parameters, TO_ITEM)); 083 return this; 084 } 085 public ICmdAdapter getCmdAdapter() { 086 return cmdAdapter; 087 } 088 public CmdItem setCmdAdapter(ICmdAdapter cmdAdapter) { 089 this.cmdAdapter = cmdAdapter; 090 return this; 091 } 092 /** 093 * 将{@code value}转为{@code type}指定的类型 094 * @param <T> 目标参数类型 095 * @param value 096 * @param type 097 * @return 098 * @see TypeUtils#cast(Object, Type, ParserConfig) 099 */ 100 @SuppressWarnings("unchecked") 101 public static final <T> T cast(Object value,Type type){ 102 return (T)TypeUtils.cast(value,type,ParserConfig.getGlobalInstance()); 103 } 104 /** 105 * 更新命令参数 106 * @param parameters 107 * @return 108 */ 109 private CmdItem updateParameter(Map<String, ?> parameters){ 110 parameters = MoreObjects.firstNonNull(parameters, Collections.<String, Object>emptyMap()); 111 for(BaseOption<Object> param : getParameters()){ 112 Object value = cast(parameters.get(param.getName()), param.javaType()); 113 param.updateFrom(value); 114 } 115 return this; 116 } 117 /** 118 * 参数 检查,如果参数为required,而输入参数中不包含这个参数,则抛出异常 119 * @param input 120 * @return input 121 */ 122 private Map<String, Object> checkRequired(Map<String, Object> input){ 123 for(BaseOption<Object> param : getParameters()){ 124 checkArgument(!param.isRequired() || input.containsKey(param.getName()), 125 "MISS REQUIRED PARAM %s",param.getName()); 126 } 127 return input; 128 } 129 /** 130 * 执行命令 131 * @return 132 * @throws CmdExecutionException 设备命令执行异常 133 */ 134 public final Object runCmd() throws CmdExecutionException{ 135 synchronized (items) { 136 if(cmdAdapter !=null){ 137 try { 138 // 将 parameter 转为 Map<String, Object> 139 Map<String, Object> objParams = Maps.transformValues(items, TO_VALUE); 140 return cmdAdapter.apply(checkRequired(objParams)); 141 } finally { 142 reset(); 143 } 144 } 145 return null; 146 } 147 } 148 /** 149 * 执行命令 150 * @param parameters 命令参数 151 * @return 152 * @throws CmdExecutionException 设备命令执行异常 153 */ 154 final Object runCmd(Map<String, ?> parameters) throws CmdExecutionException{ 155 synchronized (items) { 156 if(cmdAdapter !=null){ 157 updateParameter(parameters); 158 try { 159 // 将 parameter 转为 Map<String, Object> 160 Map<String, Object> objParams = Maps.transformValues(items, TO_VALUE); 161 return cmdAdapter.apply(checkRequired(objParams)); 162 } finally { 163 reset(); 164 } 165 } 166 return null; 167 } 168 } 169 @SuppressWarnings("unchecked") 170 public <T>BaseOption<T> getParameter(final String name){ 171 return (BaseOption<T>) getChild(name); 172 } 173 174 /** 175 * 设置所有参数为{@code null} 176 * @return 返回当前对象 177 */ 178 public CmdItem reset(){ 179 for (BaseOption<Object> item : getParameters()) { 180 item.setValue(null); 181 } 182 return this; 183 } 184 185 /** 186 * 将当前命令作为任务对象注册到指定的任务队列,可以执行队列中的任务<br> 187 * {@link #cmdAdapter}为{@code null}时无效 188 * @param queue 189 * @return 返回当前对象 190 */ 191 public CmdItem asTaskAdapter(String queue){ 192 if(cmdAdapter != null){ 193 new TaskAdapter(queue) 194 .setCmdAdapter(cmdAdapter) 195 .register(); 196 this.taskQueue = queue; 197 } 198 return this; 199 } 200 /** 201 * 将当前命令作为任务对象注册到指定的任务队列,可以执行队列中的任务<br> 202 * {@link #cmdAdapter}为{@code null}时无效 203 * @param taskAdatperClass 任务对象类,必须有(String)构造方法,应用层可以继承{@link TaskAdapter} 204 * 重写{@link TaskAdapter#makeAck(Object, Exception, String, Long)}方法,返回不同的响应对象 205 * @return 返回当前对象 206 */ 207 public CmdItem asTaskAdapter(String queue,Class<? extends TaskAdapter> taskAdatperClass){ 208 if(cmdAdapter != null){ 209 try { 210 checkNotNull(taskAdatperClass,"taskAdatperClass is null") 211 .getConstructor(String.class) 212 .newInstance(queue) 213 .setCmdAdapter(cmdAdapter) 214 .register(); 215 this.taskQueue = queue; 216 } catch (Exception e) { 217 Throwables.throwIfUnchecked(e); 218 throw new RuntimeException(e); 219 } 220 } 221 return this; 222 } 223 /** 224 * 设备命令执行接口 225 * @author guyadong 226 * 227 */ 228 public static interface ICmdAdapter { 229 /** 230 * 执行设备命令 231 * @param input 以值对(key-value)形式提供的输入参数 232 * @return 命令返回值,没有返回值则返回{@code null} 233 * @throws CmdExecutionException 命令执行失败 234 */ 235 Object apply(Map<String, Object> input) throws CmdExecutionException; 236 } 237 /** 238 * @return taskQueue 239 */ 240 public String getTaskQueue() { 241 return taskQueue; 242 } 243 244 /** 245 * @param taskQueue 要设置的 taskQueue 246 */ 247 public void setTaskQueue(String taskQueue) { 248 this.taskQueue = taskQueue; 249 } 250}