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