001package net.gdface.codegen.thrift;
002
003import java.lang.reflect.Type;
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.List;
007import java.util.Map;
008import java.util.Set;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import com.facebook.swift.codec.metadata.DecoratorThriftFieldMetadata;
014import com.facebook.swift.codec.metadata.DecoratorThriftServiceMetadata;
015import com.facebook.swift.codec.metadata.ErpcType;
016import com.facebook.swift.codec.metadata.ThriftCatalogWithTransformer;
017import com.facebook.swift.codec.metadata.ThriftFieldMetadata;
018import com.facebook.swift.service.metadata.ThriftMethodMetadata;
019import com.google.common.base.Function;
020import com.google.common.base.MoreObjects;
021import com.google.common.base.Predicate;
022import com.google.common.base.Predicates;
023import com.google.common.collect.ImmutableList;
024import com.google.common.collect.Iterables;
025import com.google.common.collect.Lists;
026import com.google.common.collect.Maps;
027import com.google.common.collect.Sets;
028import com.google.common.reflect.TypeToken;
029import net.gdface.annotation.AnnotationException;
030import net.gdface.annotation.AnnotationRuntimeException;
031import net.gdface.annotation.DeriveMethod;
032import net.gdface.annotation.ServicePort;
033import net.gdface.codegen.AnnotationUtils;
034import net.gdface.codegen.InvalidAnnotationDefineException;
035import net.gdface.codegen.InvalidNameException;
036import net.gdface.codegen.Method;
037import net.gdface.codegen.NewSourceInfoAbstract;
038import net.gdface.codegen.ServiceInfo;
039import net.gdface.codegen.thrift.ThriftServiceDecoratorConfiguration.TaskType;
040import net.gdface.codegen.thrift.metadata.CodegenPreAllocFilter;
041import net.gdface.codegen.Method.Parameter;
042import net.gdface.codegen.generator.GeneratorUtils;
043import net.gdface.thrift.ThriftUtils;
044import net.gdface.thrift.TypeTransformer;
045import net.gdface.thrift.exception.ServiceRuntimeException;
046import net.gdface.utils.NameStringUtils;
047
048import static com.google.common.base.Preconditions.*;
049
050/**
051 * thrift service 生成{@code NewSourceInfo}实现
052 * @author guyadong
053 *
054 * @param <T>
055 */
056public class ThriftServiceDecorator<T> extends NewSourceInfoAbstract<T> implements ThriftConstants {
057        private static final Logger logger = LoggerFactory.getLogger(ThriftServiceDecorator.class);
058        private ServiceInfo serviceInfo;
059        private final Map<Method,String> servicePorts = Maps.newHashMap();
060        private final TypeHelper typeHelper = new TypeHelper(this);
061        private String generatePackage;
062        private String clientInterfaceName;
063        private Boolean isUseStringInService = null;
064        private Boolean isUseBinaryInService = null;
065        private Boolean isUseVectorInService = null;
066        private Boolean isUseExceptionInService = null;
067        /**
068         * 记录接口中用到的所有容器类型List,Map,Set
069         */
070        private final Set<CxxTypeMeta> containerTypes = Sets.newHashSet(
071                        CxxType.getThriftType(ThriftUtils.mapToken(TypeToken.of(String.class), TypeToken.of(String.class)).getType()).getStubType());
072        
073        private final Set<ErpcType> collectionTypes =  Sets.newLinkedHashSet();
074        private final Predicate<Class<?>> findMap = new Predicate<Class<?>>(){
075
076                @Override
077                public boolean apply(Class<?> input) {
078                        return null == input ? false :Map.class.isAssignableFrom(input);
079                }};
080        private final Predicate<Class<?>> findSet = new Predicate<Class<?>>(){
081
082                @Override
083                public boolean apply(Class<?> input) {
084                        return null == input ? false :Set.class.isAssignableFrom(input);
085                }};
086        private final CxxType cxxType;
087        private final CxxTypeMeta stubCxxTypeMeta;
088        private final String thriftClientPackage;
089        private final Predicate<Method> tagsFilter;
090        private volatile DecoratorThriftServiceMetadata thriftServiceMetadata;
091        private Class<?> thriftServiceClass;
092        public ThriftServiceDecorator(Class<T> interfaceClass, Class<? extends T> refClass) {
093                super(interfaceClass, refClass, null);
094                TypeHelper.VERIFYTYPE_MONITOR.set(new Predicate<Type>(){
095
096                @Override
097                public boolean apply(Type input) {
098                        if(null ==isUseVectorInService){
099                                Class<?> rawType = TypeToken.of(input).getRawType();
100                                if((rawType.isArray() && rawType.getComponentType()!=byte.class) || List.class.isAssignableFrom(rawType)){
101                                        isUseVectorInService = Boolean.TRUE;    
102                                }
103                        }
104                        if(input instanceof Class<?>){
105                                if(null == isUseStringInService){
106                                        if(GeneratorUtils.isBinary(input)){
107                                                isUseStringInService = Boolean.TRUE;
108                                        }
109                                }
110                                if(null ==isUseBinaryInService && TaskType.ERPC_PROXY.equals(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType())){
111                                        if(GeneratorUtils.isBinary(input)){
112                                                isUseBinaryInService = Boolean.TRUE;
113                                        }
114                                }
115                                if(null ==isUseExceptionInService){
116                                        if(Exception.class.isAssignableFrom((Class<?>) input)){
117                                                isUseExceptionInService = Boolean.TRUE; 
118                                        }                                       
119                                }
120                        }
121                        try{
122                                CxxTypeMeta meta = CxxType.getThriftType(input).getUiType();
123                                if(meta.isContainer()){
124                                        containerTypes.add(meta);
125                                }
126                        }catch(Exception e){
127                                logger.debug(e.getMessage());
128                        }
129                        return false;
130                }});
131                thriftClientPackage = ThriftServiceDecoratorConfiguration.INSTANCE.getThriftClientPackage();
132                clientInterfaceName = thriftClientPackage + "." + this.interfaceClass.getSimpleName();
133                cxxType = CxxType.struct(interfaceClass);
134                stubCxxTypeMeta = cxxType.getStubType().cast("::"+CxxHelper.cxxNamespace(clientInterfaceName,true));
135                tagsFilter = new Predicate<Method>(){
136                        @Override
137                        public boolean apply(Method input) {
138                                return input.tagsMatched(ThriftServiceDecoratorConfiguration.INSTANCE.getReqiredTags());
139                        }};
140        }
141
142        @Override
143        public boolean compile() {
144                boolean compileOk=false;
145                try{
146                        super.compile();
147                        serviceInfo=new ServiceInfo(AnnotationUtils.getServiceAnnotation(interfaceClass));
148                        for (Method method : methodsNeedGenerated) {
149                                compile(method);
150                        }
151                        if(typeHelper.needTransformer()){
152                                addImportedClass(TypeTransformer.class);
153                        }
154                        addImportedClass(ServiceRuntimeException.class);
155                        compileOk = true;
156                        TypeHelper.VERIFYTYPE_MONITOR.remove();
157                } catch (AnnotationException e) {
158                        logger.error(e.toString());
159                } catch(AnnotationRuntimeException e){
160                        logger.error(e.toString());
161                } 
162                return compileOk;
163        }
164        private final void compile(Method method) throws InvalidNameException {
165                TypeHelper.checkNotGeneric(method);
166                checkPortSuffix(method);
167                String portName = getPortName(method);
168                // 检查方法是否重名
169                if(servicePorts.containsKey(portName)){
170                        throw new InvalidNameException(String.format("duplicated method name %s", portName));
171                }
172                typeHelper.checkType(method);
173                typeHelper.addReferTypes(method);
174                servicePorts.put(method,portName);
175                if(NameStringUtils.isThriftReserved( portName)){
176                        logger.warn("portName(method name) '{}' of '{}' is thrift IDL reserved word", portName,this.getInterfaceClass().getName());
177                }
178                for (Parameter param : method.getParameters()) {
179                        if(NameStringUtils.isThriftReserved( param.getName())){
180                                logger.warn("parameter name '{}' of method '{}' is thrift IDL reserved word", param.getName(),portName);
181                        }
182                }
183        }
184
185        private static void checkPortSuffix(Method method) throws InvalidNameException{
186                ServicePort servicePort = method.getAnnotation(ServicePort.class);
187                if (null != servicePort) {
188                        // 检查名字是否合法,不允许包含空格,只能是数字字母下划线
189                        if (!(servicePort.suffix().isEmpty() || servicePort.suffix().matches(AnnotationUtils.PATTERN_NOT_BLANK))){
190                                throw new InvalidNameException(
191                                                String.format("the suffix of annotation [%s] in method %s of %s must not have space char ",
192                                                                ServicePort.class.getSimpleName(), 
193                                                                method.getName(), 
194                                                                method.getDeclaringClass().getSimpleName()));
195                        }
196                }
197        }
198        
199        @Override
200        protected void checkClass(Class<T> interfaceClass, Class<? extends T> refClass, Class<? extends T> baseClass) {
201                checkNotNull(interfaceClass,"interfaceClass is null");
202                if (!interfaceClass.isInterface()){
203                        throw new IllegalArgumentException("interfaceClass must be Interface(必须是接口)");
204                }
205                TypeHelper.checkNotGeneric(interfaceClass);
206                if(null != refClass ){
207                        if ( !interfaceClass.isAssignableFrom(refClass) 
208                                        || refClass.isInterface()){
209                                throw new IllegalArgumentException(String.format(
210                                                "refClass must  implement of [%s] (必须实现接口)", interfaceClass.getName()));
211                        }
212                        if (!isFullImplemented(interfaceClass,refClass)){
213                                logger.warn("{} not  implement all methods of [{}] (没有实现接口所有方法)",
214                                                refClass.getName(),
215                                                interfaceClass.getName());
216                        }
217                }
218        }
219
220        @Override
221        protected void createMethodsNeedGenerated() {
222                List<Method> methods = Lists.transform(Arrays.asList(interfaceClass.getMethods()), 
223                                new Function<java.lang.reflect.Method, Method>() {
224                                        @Override
225                                        public Method apply(java.lang.reflect.Method input) {
226                                                return new Method(input,
227                                                                paramTable.getParameterNamesUnchecked(input.getName(), input.getParameterTypes()));
228                                        }
229                });
230                
231                // tags 过滤
232                Iterable<Method> filteredByTags = Iterables.filter(methods, tagsFilter);
233                ArrayList<Method> filtered = 
234                                Lists.newArrayList(
235                                                Iterables.filter(filteredByTags, Predicates.compose(new MethodFilter(interfaceClass), Method.TO_REFLECT_METHOD)));
236                methodsNeedGenerated.addAll(filtered);
237        }
238
239        public ServiceInfo getServiceInfo() {
240                return serviceInfo;
241        }
242        public boolean isTargetType(Class<?> type){
243                return serviceInfo.getTargetType()==type;
244        }
245        public List<ThriftStructDecorator> getDecorateTypes() {
246                return typeHelper.getDecorateTypes();
247        }
248        public List<ThriftStructDecorator> getThriftTypes() {
249                return typeHelper.getThriftTypes();
250        }
251        public String toThriftType(Type type) {
252                return typeHelper.toThriftType(type);
253        }
254        public String toClientThriftType(Type type) {
255                return typeHelper.toClientThriftType(type);
256        }
257        public String toClientThriftyType(Type type) {
258                return typeHelper.toClientThriftyType(type);
259        }
260        public boolean isClientThriftType(Type type) {
261                return typeHelper.isClientThriftType(type);
262        }
263
264        public String toThriftyDecoratorType(Type type) {
265                return typeHelper.toThriftyDecoratorType(type);
266        }
267
268        public String getClientInterfaceName(){
269                return clientInterfaceName;
270        }
271        public String getPortName(Method method){
272                ServicePort servicePort = method.getAnnotation(ServicePort.class);
273                String portName = method.getName();
274                if (null != servicePort) {
275                        portName += servicePort.suffix();
276                }
277                return portName;
278        }
279
280        public String getGeneratePackage() {
281                return generatePackage;
282        }
283
284        public void setGeneratePackage(String generatePackage) {
285                this.generatePackage = generatePackage;
286        }
287        public void removeDecorateTypesFromImports(){
288                // 排除枚举类型
289                Iterable<Class<?>> it = Iterables.filter(typeHelper.getTypesWithDecorator(),new Predicate<Class<?>>() {
290                        @Override
291                        public boolean apply(Class<?> input) {
292                                return !input.isEnum();
293                        }
294                });
295                ArrayList<Class<?>> types = Lists.newArrayList(it);
296                this.removeClassFromImports(types);
297        }
298        public void removeExceptionsFromImports(){
299                this.removeClassFromImports(typeHelper.getReferExceptions());
300        }
301
302        public List<Class<?>> getReferExceptions() {
303                return typeHelper.getReferExceptions();
304        }
305        public boolean isUseStringInService(){
306                return Boolean.TRUE.equals(isUseStringInService);
307        }
308        public boolean isUseBinaryInService(){
309                return Boolean.TRUE.equals(isUseBinaryInService);
310        }
311        public boolean isUseMapInService(){
312                return Iterables.tryFind(getImportedList().values(), findMap ).isPresent();
313        }
314        public boolean isUseSetInService(){
315                return Iterables.tryFind(getImportedList().values(), findSet).isPresent();
316        }
317        public boolean isUseVectorInService(){
318                return Boolean.TRUE.equals(isUseVectorInService);
319        }
320        public boolean isUseExceptionInService(){
321                return Boolean.TRUE.equals(isUseExceptionInService);
322        }
323        /**
324         * 以接口名为基础创建不同后缀的{@link CxxTypeMeta}
325         * @param suffix
326         * @return
327         */
328        public CxxTypeMeta makeStubCxxTypeMetaBySuffix(String suffix){
329                suffix = MoreObjects.firstNonNull(suffix, "");
330                return stubCxxTypeMeta.castName(this.getInterfaceClass().getSimpleName()+suffix);
331        }
332
333        public CxxType getCxxType() {
334                return cxxType;
335        }
336
337        public CxxTypeMeta getStubCxxTypeMeta() {
338                return stubCxxTypeMeta;
339        }
340
341        public String getThriftClientNamespace() {
342                return "::"+CxxHelper.cxxNamespace(thriftClientPackage);
343        }
344
345        public Set<CxxTypeMeta> getContainerTypes() {
346                return containerTypes;
347        }
348        public DeriveMethod getDeriveMethodAnnotation(Method method) {
349                try {
350                        return AnnotationUtils.getDeriveMethodAnnotation(method, serviceInfo);
351                } catch (InvalidNameException e) {
352                        throw new RuntimeException(e);
353                } catch (InvalidAnnotationDefineException e) {
354                        throw new RuntimeException(e);
355                }
356        }
357        /**
358         * 返回 {@link DeriveMethod} 注释指定的方法名后缀,没有则返回空
359         * @param method
360         * @return
361         */
362        public String methodSuffix(Method method){
363                DeriveMethod deriveMethod = getDeriveMethodAnnotation(method);
364                if(deriveMethod !=null ){
365                        String[] suffixs = deriveMethod.methodSuffix();
366                        return suffixs != null && suffixs.length>0 ? suffixs[0]:"";
367                }
368                return "";
369        }
370
371        public DecoratorThriftServiceMetadata getThriftServiceMetadata(){
372                if(thriftServiceMetadata == null){
373                        synchronized (this) {
374                                if(thriftServiceMetadata == null){
375                                        thriftServiceMetadata = new DecoratorThriftServiceMetadata(checkNotNull(thriftServiceClass,"thriftServiceClass is uninitialized"),
376                                                        ThriftCatalogWithTransformer.CATALOG,
377                                                        ThriftServiceDecoratorConfiguration.INSTANCE.getExcludeMethods().get(getInterfaceClass()),
378                                                        ThriftServiceDecoratorConfiguration.INSTANCE.getIncludeMethods().get(getInterfaceClass()));
379                                }
380                        }
381                }
382                return thriftServiceMetadata;
383        }
384        
385        public ThriftMethodMetadata getThriftMethodMetadata(Method method){
386                if(method != null){
387                        String name = method.getName() + methodSuffix(method);
388                        return getThriftServiceMetadata().getMethod(name);
389                }
390                return null;
391        }
392        public List<ThriftFieldMetadata> getParameterMetadata(Method method){
393                ThriftMethodMetadata methodMetadata = getThriftMethodMetadata(method);
394                if(methodMetadata != null){
395                        return methodMetadata.getParameters();
396                }
397                return ImmutableList.of();
398        }
399
400        /**
401         * @return thriftServiceClass
402         */
403        public Class<?> getThriftServiceClass() {
404                return thriftServiceClass;
405        }
406
407        /**
408         * @param thriftServiceClass 要设置的 thriftServiceClass
409         * @return 
410         */
411        public ThriftServiceDecorator<T> setThriftServiceClass(Class<?> thriftServiceClass) {
412                this.thriftServiceClass = thriftServiceClass;
413                return this;
414        }
415                
416        public ThriftServiceDecorator<T> erpcProxyInit(){
417                if(TaskType.ERPC_PROXY.equals(ThriftServiceDecoratorConfiguration.INSTANCE.getTaskType())){
418                        DecoratorThriftServiceMetadata metadata = getThriftServiceMetadata();
419                        for(ThriftMethodMetadata methodMetadata:metadata.getMethods().values()){
420                                for(ThriftFieldMetadata param:methodMetadata.getParameters()){
421                                        if(GeneratorUtils.isCollection(param.getThriftType().getJavaType())){
422                                                if(!CodegenPreAllocFilter.PREALLOC_FILTER.apply((DecoratorThriftFieldMetadata) param)){
423                                                        collectionTypes.add(((DecoratorThriftFieldMetadata) param).getErpcType());
424                                                }
425                                        }
426                                }
427                                if(GeneratorUtils.isCollection(methodMetadata.getReturnType().getJavaType())){
428                                        collectionTypes.add(ErpcType.getErpcType(methodMetadata.getReturnType()));
429                                }
430                        }               
431                }
432                return this;
433        }
434
435        /**
436         * @return collectionTypes
437         */
438        public List<ErpcType> getCollectionTypes() {
439                return Lists.newArrayList(collectionTypes);
440        } 
441}