001package net.gdface.codegen.thrift; 002 003import org.apache.commons.cli.CommandLine; 004import org.apache.commons.cli.Options; 005import org.apache.commons.cli.ParseException; 006import org.apache.commons.configuration2.Configuration; 007import org.apache.commons.configuration2.PropertiesConfiguration; 008import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; 009import org.apache.commons.configuration2.io.FileHandler; 010 011import static com.google.common.base.Preconditions.checkArgument; 012import static com.google.common.base.Preconditions.checkState; 013 014import com.google.common.base.Strings; 015import com.google.common.base.Throwables; 016import com.google.common.collect.ImmutableMap; 017import com.google.common.collect.Lists; 018import com.google.common.collect.Maps; 019import com.google.common.collect.Sets; 020 021import net.gdface.codegen.generator.CodeWriter; 022import net.gdface.codegen.generator.CxxCodeWriter; 023import net.gdface.codegen.generator.GeneratorConfiguration; 024import net.gdface.codegen.generator.JavaCodeWriter; 025import net.gdface.utils.ConditionChecks; 026import net.gdface.utils.MiscellaneousUtils; 027import net.gdface.utils.SimpleLog; 028 029import java.io.File; 030import java.util.Collections; 031import java.util.List; 032import java.util.Map; 033import java.util.Map.Entry; 034import java.util.Set; 035 036/** 037 * decorator生成器参数对象 038 * @author guyadong 039 * 040 */ 041public class ThriftServiceDecoratorConfiguration extends GeneratorConfiguration 042 implements ThriftConstants{ 043 private static final String ENCODING = "UTF-8"; 044 /** 045 * 任务类型定义 046 * @author guyadong 047 * 048 */ 049 public static enum TaskType{ 050 /** 生成thrift service代码 */SERVICE("service", true), 051 /** 生成thrift/swift client代码 */CLIENT("client", false), 052 /** 生成基于Microsoft/thrifty client代码 */CLIENT_THRIFTY("client_thrifty", false), 053 /** 生成eRPC代理服务代码 */ERPC_PROXY("erpc_proxy", false); 054 /** 对应的模板文件路径前缀 */ 055 public final String folder; 056 /** 是否转换引用类型 */ 057 public final boolean castReferType; 058 private TaskType(String folder, boolean castReferType){ 059 this.folder = folder; 060 this.castReferType = castReferType; 061 } 062 }; 063 /** 064 * 生成代码语言类型定义 065 * @author guyadong 066 * 067 */ 068 public static enum LanguageType{ 069 /** 生成thrift java代码 */JAVA(""), 070 /** 生成thrift c++代码 */CPP("_cpp"), 071 /** 生成thrift c++代码 */C_GLIB("_c_glib") 072 ; 073 private static LanguageType current = null; 074 /** 模板根文件夹名结尾 */ 075 public final String postfix; 076 private LanguageType(String postfix){ 077 this.postfix = postfix; 078 } 079 /** 080 * 返回对应的 {@link CodeWriter}实例 081 * @param outputFolder 082 */ 083 public CodeWriter getCodeWriter(File outputFolder){ 084 switch(this){ 085 case JAVA: 086 return new JavaCodeWriter(outputFolder); 087 case CPP: 088 return new CxxCodeWriter(outputFolder); 089 case C_GLIB: 090 return new CxxCodeWriter(outputFolder); 091 default: 092 throw new UnsupportedOperationException("unsupported language type:" + this.name()); 093 } 094 } 095 public static LanguageType getCurrent() { 096 return current; 097 } 098 public synchronized static void setCurrent(LanguageType current) { 099 checkState(null == LanguageType.current 100 || LanguageType.current.equals(current), 101 "LanguageType.current can be initialized only onece"); 102 LanguageType.current = current; 103 } 104 }; 105 public static final String DEFAULT_TEMPLATE_FOLDER = "thrift"; 106 public static final String DEFAULT_LANGUAGE = "JAVA"; 107 /** refClass 的默认值 */ 108 public static final Class<?> DEF_REF_CLASS = Object.class; 109 private static final String NO_REF_CLASS = ""; 110 private static final Map<String, String> defaultExcludeFields = ImmutableMap.of("gu.sql2java.BaseBean","modified,initialized"); 111 /** interface class -- reference class */ 112 private Map<Class<?>, Class<?>> interfaceClasses; 113 /** interface class -- thrift service class */ 114 private Map<Class<?>, Class<?>> thriftServiceClasses; 115 private TaskType taskType; 116 private LanguageType languageType; 117 private String thriftClientPackage; 118 private String sourcepath; 119 private String classpath; 120 private final PropertiesConfiguration config = new PropertiesConfiguration(); 121 private Set<String> reqiredTags = Collections.emptySet(); 122 private Set<String> commonTypes = Collections.emptySet(); 123 private String programName; 124 private String portPrefix; 125 private Map<Class<?>, List<String>> excludeMethods = Maps.newHashMap(); 126 private Map<Class<?>, List<String>> includeMethods = Maps.newHashMap(); 127 128 private final Map<Class<?>, List<String>> excludeFields; 129 private int erpcForwardPort; 130 private int erpcProxyPort; 131 private int defaultMaxLength=256; 132 private int errmsgMaxLength=256; 133 private int binaryOutputSize=256; 134 public static final ThriftServiceDecoratorConfiguration INSTANCE = new ThriftServiceDecoratorConfiguration(); 135 private ThriftServiceDecoratorConfiguration() { 136 super(); 137 // 指定模板文件夹 138 this.defaultValue.setProperty(TEMPLATE_FOLDER_OPTION_LONG, DEFAULT_TEMPLATE_FOLDER); 139 // 指定refClass的默认值,避免默认值为{@code null} 140 this.defaultValue.setProperty(REFERENCE_CLASS_OPTION_LONG,NO_REF_CLASS); 141 // 指定refClass的默认值,避免默认值为{@code null} 142 this.defaultValue.setProperty(THRIFT_CLIENT_PKG_OPTION_LONG,""); 143 // 指定refClass的默认值,避免默认值为{@code null} 144 this.defaultValue.setProperty(LANGUAGE_OPTION_LONG,DEFAULT_LANGUAGE); 145 // 指定CONFIG的默认值,避免默认值为{@code null} 146 this.defaultValue.setProperty(CONFIG_OPTION_LONG,null); 147 // 指定sourcepath的默认值,避免默认值为{@code null} 148 this.defaultValue.setProperty(SOURCE_PREFIX_OPTION_LONG,""); 149 // 指定classpath的默认值,避免默认值为{@code null} 150 this.defaultValue.setProperty(CLASS_PATH_OPTION_LONG,""); 151 // 指定TAGS的默认值,避免默认值为{@code null} 152 this.defaultValue.setProperty(TAGS_OPTION_LONG,""); 153 // 指定COMMON_TYPES的默认值,避免默认值为{@code null} 154 this.defaultValue.setProperty(COMMON_TYPES_OPTION_LONG,""); 155 // 指定programName的默认值,避免默认值为{@code null} 156 this.defaultValue.setProperty(ERPC_PROGRAM_OPTION_LONG,""); 157 // 指定portPrefix的默认值,避免默认值为{@code null} 158 this.defaultValue.setProperty(ERPC_PORT_PREFIX_OPTION_LONG,""); 159 // 指定excludeMethods的默认值,避免默认值为{@code null} 160 this.defaultValue.setProperty(EXCLUDE_METHODS_OPTION_LONG,new String[0]); 161 // 指定includeMethods的默认值,避免默认值为{@code null} 162 this.defaultValue.setProperty(INCLUDE_METHODS_OPTION_LONG,new String[0]); 163 // 指定excludeFields的默认值,避免默认值为{@code null} 164 this.defaultValue.setProperty(EXCLUDE_FIELDS_OPTION_LONG,new String[0]); 165 166 // 指定excludeFields的默认值,避免默认值为{@code null} 167 this.defaultValue.setProperty(THRIFT_SERVICE_CLASS_OPTION_LONG,""); 168 169 excludeFields = Maps.newHashMap(); 170 for(Entry<String, String> entry : defaultExcludeFields.entrySet()){ 171 try { 172 excludeFields.put(Class.forName(entry.getKey()), MiscellaneousUtils.elementsOf(entry.getValue())); 173 } catch (ClassNotFoundException e) { 174 SimpleLog.log(e.toString()); 175 } 176 } 177 178 // 指定erpcForwardPort的默认值,避免默认值为{@code null} 179 this.defaultValue.setProperty(ERPC_FORWARD_PORT_OPTION_LONG,0); 180 // 指定erpcProxyPort的默认值,避免默认值为{@code null} 181 this.defaultValue.setProperty(ERPC_PROXY_PORT_OPTION_LONG,0); 182 // 指定defaultMaxLength的默认值,避免默认值为{@code null} 183 this.defaultValue.setProperty(ERPC_DEFAULT_MAX_LENGTH_OPTION_LONG,256); 184 // 指定errmsgMaxLength的默认值,避免默认值为{@code null} 185 this.defaultValue.setProperty(ERPC_ERRMSG_MAX_LENGTH_OPTION_LONG,256); 186 // 指定binaryOutputSize的默认值,避免默认值为{@code null} 187 this.defaultValue.setProperty(ERPC_BINARY_OUTPUT_SIZE_OPTION_LONG,256); 188 } 189 190 @Override 191 public void loadConfig(Options options, CommandLine cmd) throws ParseException { 192 super.loadConfig(options, cmd); 193 try { 194 List<Class<?>> interfaceList = toClassArray((String) getProperty(INTERFACE_CLASS_OPTION_LONG)); 195 List<Class<?>> refList = toClassArray((String) getProperty(REFERENCE_CLASS_OPTION_LONG)); 196 this.thriftClientPackage = (String)getProperty(THRIFT_CLIENT_PKG_OPTION_LONG); 197 if(refList.size()>0 && refList.size() != interfaceList.size()){ 198 throw new ParseException("mismatch number interface class and reference class"); 199 } 200 interfaceClasses = Maps.newLinkedHashMap(); 201 for(int i = 0 ;i < interfaceList.size() ; ++i){ 202 Class<?> key = interfaceList.get(i); 203 if(interfaceList.get(i)!=DEF_REF_CLASS){ 204 try{ 205 Class<?> value = refList.get(i); 206 interfaceClasses.put(key, value); 207 }catch(IndexOutOfBoundsException e){ 208 interfaceClasses.put(key, DEF_REF_CLASS); 209 } 210 } 211 } 212 if(interfaceList.isEmpty()){ 213 throw new ParseException("NOT FOUND VALID interface class define"); 214 } 215 } catch (ClassNotFoundException e) { 216 throw new ParseException("ClassNotFoundException:"+e.getMessage()); 217 } 218 try{ 219 taskType = TaskType.valueOf((String) getProperty(TASK_TYPE_OPTION_LONG)); 220 if((taskType == TaskType.CLIENT || taskType == TaskType.CLIENT_THRIFTY) && this.thriftClientPackage.isEmpty()){ 221 throw new IllegalArgumentException(String.format("must set param :%s",THRIFT_CLIENT_PKG_OPTION_LONG)); 222 } 223 }catch(IllegalArgumentException e){ 224 throw new ParseException(e.getMessage()); 225 } 226 try{ 227 languageType = LanguageType.valueOf((String) getProperty(LANGUAGE_OPTION_LONG)); 228 LanguageType.setCurrent(languageType); 229 }catch(IllegalArgumentException e){ 230 throw new ParseException(e.getMessage()); 231 } 232 if(hasProperty(CONFIG_OPTION_LONG)){ 233 File configFile = new File((String) getProperty(CONFIG_OPTION_LONG)); 234 checkArgument(configFile.isFile() && configFile.getName().endsWith(".properties"), 235 "%s must be a .properties file",CONFIG_OPTION_LONG); 236 try { 237 // 指定文件编码方式,否则properties文件读取中文会是乱码,要求文件编码是UTF-8 238 FileBasedConfigurationBuilder.setDefaultEncoding(PropertiesConfiguration.class, ENCODING); 239 new FileHandler(config).load(configFile); 240 } catch (Exception e) { 241 Throwables.throwIfUnchecked(e); 242 throw new RuntimeException(e); 243 } 244 } 245 sourcepath = getProperty(SOURCE_PREFIX_OPTION_LONG); 246 classpath = getProperty(CLASS_PATH_OPTION_LONG); 247 // 允许 , 做分隔符 248 classpath = classpath.replaceAll(",", File.pathSeparator); 249 // linux下允许用 ;号做分隔符 250 if(File.pathSeparatorChar != ';'){ 251 classpath = classpath.replaceAll(";", File.pathSeparator); 252 } 253 String tags = getProperty(TAGS_OPTION_LONG); 254 if(!Strings.isNullOrEmpty(tags)){ 255 this.reqiredTags = Sets.newHashSet(MiscellaneousUtils.elementsOf(tags)); 256 } 257 String types = getProperty(COMMON_TYPES_OPTION_LONG); 258 if(!Strings.isNullOrEmpty(types)){ 259 this.commonTypes = Sets.newHashSet(MiscellaneousUtils.elementsOf(types)); 260 } 261 String program = getProperty(ERPC_PROGRAM_OPTION_LONG); 262 if(Strings.isNullOrEmpty(program)){ 263 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY),ParseException.class,"must define argument: %s",ERPC_PROGRAM_OPTION_LONG); 264 }else{ 265 this.programName = program; 266 } 267 String prefix = getProperty(ERPC_PORT_PREFIX_OPTION_LONG); 268 if(Strings.isNullOrEmpty(prefix)){ 269 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY),ParseException.class,"must define argument: %s",ERPC_PORT_PREFIX_OPTION_LONG); 270 }else{ 271 this.portPrefix = prefix; 272 } 273 String[] excludeMethodNames = getProperty(EXCLUDE_METHODS_OPTION_LONG); 274 if(excludeMethodNames != null){ 275 for(String str : excludeMethodNames){ 276 String[] entry = str.split(":"); 277 try { 278 List<String> names = MiscellaneousUtils.elementsOf(entry[1]); 279 if(!names.isEmpty()){ 280 excludeMethods.put(Class.forName(entry[0]),names); 281 } 282 } catch (ClassNotFoundException e) { 283 SimpleLog.log(e.toString()); 284 } 285 } 286 } 287 String[] inludeMethodNames = getProperty(INCLUDE_METHODS_OPTION_LONG); 288 if(inludeMethodNames != null){ 289 for(String str : inludeMethodNames){ 290 String[] entry = str.split(":"); 291 try { 292 List<String> names = MiscellaneousUtils.elementsOf(entry[1]); 293 if(!names.isEmpty()){ 294 includeMethods.put(Class.forName(entry[0]),names); 295 } 296 } catch (ClassNotFoundException e) { 297 SimpleLog.log(e.toString()); 298 } 299 } 300 } 301 String[] excludeFieldsEntry = getProperty(EXCLUDE_FIELDS_OPTION_LONG); 302 if(excludeFieldsEntry != null){ 303 for(String str : excludeFieldsEntry){ 304 String[] entry = str.split(":"); 305 try { 306 List<String> names = MiscellaneousUtils.elementsOf(entry[1]); 307 if(!names.isEmpty()){ 308 excludeFields.put(Class.forName(entry[0]),names); 309 } 310 } catch (ClassNotFoundException e) { 311 SimpleLog.log(e.toString()); 312 } 313 } 314 } 315 316 try { 317 List<Class<?>> interfaceList = toClassArray((String) getProperty(INTERFACE_CLASS_OPTION_LONG)); 318 List<Class<?>> tsList = toClassArray((String) getProperty(THRIFT_SERVICE_CLASS_OPTION_LONG)); 319 ImmutableMap.Builder<Class<?>, Class<?>> builer= ImmutableMap.builder(); 320 if(!tsList.isEmpty()){ 321 ConditionChecks.checkTrue(tsList.size() == interfaceList.size(), 322 ParseException.class, 323 "mismatch number interface class and thrift service class"); 324 for(int i=0; i<tsList.size(); ++i){ 325 builer.put(interfaceList.get(i), tsList.get(i)); 326 } 327 }else{ 328 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY), 329 ParseException.class, 330 "must define argument: %s for ERPC_PROXY task",THRIFT_SERVICE_CLASS_OPTION_LONG); 331 } 332 thriftServiceClasses = builer.build(); 333 } catch (ClassNotFoundException e) { 334 throw new ParseException("ClassNotFoundException:"+e.getMessage()); 335 } 336 337 erpcForwardPort = ((Number)getProperty(ERPC_FORWARD_PORT_OPTION_LONG)).intValue(); 338 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY) || erpcForwardPort>0, 339 ParseException.class, 340 "must define argument: %s for ERPC_PROXY task",ERPC_FORWARD_PORT_OPTION_LONG); 341 342 erpcProxyPort = ((Number)getProperty(ERPC_PROXY_PORT_OPTION_LONG)).intValue(); 343 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY) || erpcProxyPort>0, 344 ParseException.class, 345 "must define argument: %s for ERPC_PROXY task",ERPC_PROXY_PORT_OPTION_LONG); 346 347 defaultMaxLength = ((Number)getProperty(ERPC_DEFAULT_MAX_LENGTH_OPTION_LONG)).intValue(); 348 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY) || defaultMaxLength>0, 349 ParseException.class, 350 "must define argument: %s for ERPC_PROXY task",ERPC_DEFAULT_MAX_LENGTH_OPTION_LONG); 351 352 errmsgMaxLength = ((Number)getProperty(ERPC_ERRMSG_MAX_LENGTH_OPTION_LONG)).intValue(); 353 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY) || errmsgMaxLength>0, 354 ParseException.class, 355 "must define argument: %s for ERPC_PROXY task",ERPC_ERRMSG_MAX_LENGTH_OPTION_LONG); 356 357 binaryOutputSize = ((Number)getProperty(ERPC_BINARY_OUTPUT_SIZE_OPTION_LONG)).intValue(); 358 ConditionChecks.checkTrue(!taskType.equals(TaskType.ERPC_PROXY) || binaryOutputSize>0, 359 ParseException.class, 360 "must define argument: %s for ERPC_PROXY task",ERPC_BINARY_OUTPUT_SIZE_OPTION_LONG); 361 362 } 363 364 private List<Class<?>> toClassArray(String input) throws ClassNotFoundException{ 365 String[] classNames = input.split(","); 366 List<Class<?>>result = Lists.newArrayList(); 367 for(String name:classNames){ 368 result.add(name.isEmpty()? DEF_REF_CLASS : Class.forName(name)); 369 } 370 return result; 371 } 372 373 public Map<Class<?>, Class<?>> getInterfaceClasses() { 374 return interfaceClasses; 375 } 376 377 public Map<Class<?>, Class<?>> getThriftServiceClasses() { 378 return thriftServiceClasses; 379 } 380 381 public TaskType getTaskType() { 382 return taskType; 383 } 384 385 public LanguageType getLanguageType() { 386 return languageType; 387 } 388 389 @Override 390 public String getTemplateFolder() { 391 String folder = super.getTemplateFolder(); 392 StringBuilder sb = new StringBuilder(folder); 393 if(DEFAULT_TEMPLATE_FOLDER.equals(folder)){ 394 sb.append(getLanguageType().postfix); 395 } 396 sb.append("/").append(getTaskType().folder); 397 return sb.toString(); 398 } 399 400 public String getThriftClientPackage() { 401 return thriftClientPackage; 402 } 403 public CodeWriter getCodeWriter(){ 404 return this.getLanguageType().getCodeWriter(getOutputLocation()); 405 } 406 407 /** 408 * @return config 409 */ 410 public Configuration getConfig() { 411 return config; 412 } 413 /** 414 * @return sourcepath 415 */ 416 public String getSourcepath() { 417 return sourcepath; 418 } 419 420 /** 421 * @return classpath 422 */ 423 public String getClasspath() { 424 return classpath; 425 } 426 427 public Set<String> getReqiredTags() { 428 return reqiredTags; 429 } 430 431 public Set<String> getCommonTypes() { 432 return commonTypes; 433 } 434 435 /** 436 * @return programName 437 */ 438 public String getProgramName() { 439 return programName; 440 } 441 442 /** 443 * @return portPrefix 444 */ 445 public String getPortPrefix() { 446 return portPrefix; 447 } 448 449 /** 450 * @return excludeMethods 451 */ 452 public Map<Class<?>, List<String>> getExcludeMethods() { 453 return excludeMethods; 454 } 455 456 /** 457 * @return includeMethods 458 */ 459 public Map<Class<?>, List<String>> getIncludeMethods() { 460 return includeMethods; 461 } 462 463 /** 464 * @return excludeFields 465 */ 466 public Map<Class<?>, List<String>> getExcludeFields() { 467 return excludeFields; 468 } 469 470 /** 471 * @return erpcForwardPort 472 */ 473 public int getErpcForwardPort() { 474 return erpcForwardPort; 475 } 476 477 /** 478 * @return erpcProxyPort 479 */ 480 public int getErpcProxyPort() { 481 return erpcProxyPort; 482 } 483 484 /** 485 * @return defaultMaxLength 486 */ 487 public int getDefaultMaxLength() { 488 return defaultMaxLength; 489 } 490 491 /** 492 * @return errmsgMaxLength 493 */ 494 public int getErrmsgMaxLength() { 495 return errmsgMaxLength; 496 } 497 498 /** 499 * @return binaryOutputSize 500 */ 501 public int getBinaryOutputSize() { 502 return binaryOutputSize; 503 } 504 505}