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 boolean isDecoratorType(Type type) {
220                return typeHelper.isDecoratorType(type);
221        }
222
223        public List<ThriftStructDecorator> getDecorateTypes() {
224                return typeHelper.getDecorateTypes();
225        }
226
227        public String toThriftType(Type type) {
228                return typeHelper.toThriftType(type);
229        }
230        public String toClientThriftType(Type type) {
231                return typeHelper.toClientThriftType(type);
232        }
233        public String toClientThriftyType(Type type) {
234                return typeHelper.toClientThriftyType(type);
235        }
236        public boolean isClientThriftType(Type type) {
237                return typeHelper.isClientThriftType(type);
238        }
239
240        public String toDecoratorType(Type type) {
241                return typeHelper.toDecoratorType(type);
242        }
243
244        public String toThriftyDecoratorType(Type type) {
245                return typeHelper.toThriftyDecoratorType(type);
246        }
247
248        public String getClientInterfaceName(){
249                return clientInterfaceName;
250        }
251        public String getPortName(Method method){
252                ServicePort servicePort = method.getAnnotation(ServicePort.class);
253                String portName = method.getName();
254                if (null != servicePort) {
255                        portName += servicePort.suffix();
256                }
257                return portName;
258        }
259
260        public String getGeneratePackage() {
261                return generatePackage;
262        }
263
264        public void setGeneratePackage(String generatePackage) {
265                this.generatePackage = generatePackage;
266        }
267        public void removeDecorateTypesFromImports(){
268                this.removeClassFromImports(typeHelper.getTypesWithDecorator());
269        }
270        public void removeExceptionsFromImports(){
271                this.removeClassFromImports(typeHelper.getReferExceptions());
272        }
273
274        public List<Class<?>> getReferExceptions() {
275                return typeHelper.getReferExceptions();
276        }
277        public boolean isUseStringInService(){
278                return Boolean.TRUE.equals(isUseStringInService);
279        }
280        public boolean isUseMapInService(){
281                return Iterables.tryFind(getImportedList().values(), findMap ).isPresent();
282        }
283        public boolean isUseSetInService(){
284                return Iterables.tryFind(getImportedList().values(), findSet).isPresent();
285        }
286        public boolean isUseVectorInService(){
287                return Boolean.TRUE.equals(isUseVectorInService);
288        }
289        public boolean isUseExceptionInService(){
290                return Boolean.TRUE.equals(isUseExceptionInService);
291        }
292        /**
293         * 以接口名为基础创建不同后缀的{@link CxxTypeMeta}
294         * @param suffix
295         * @return
296         */
297        public CxxTypeMeta makeStubCxxTypeMetaBySuffix(String suffix){
298                suffix = MoreObjects.firstNonNull(suffix, "");
299                return stubCxxTypeMeta.castName(this.getInterfaceClass().getSimpleName()+suffix);
300        }
301
302        public CxxType getCxxType() {
303                return cxxType;
304        }
305
306        public CxxTypeMeta getStubCxxTypeMeta() {
307                return stubCxxTypeMeta;
308        }
309        public CxxTypeMeta getThriftCxxTypeMeta(Type type) {
310                CxxTypeMeta meta = CxxType.getThriftType(type).getStubType();
311                return meta.castNamespace("::"+CxxHelper.cxxNamespace(thriftClientPackage,true)) ;
312        }
313        /**
314         * 判断指定的类型是否需要类型转换
315         * @param type
316         * @return
317         */
318        public boolean needCast(Type type){
319                return !CxxType.getThriftType(type).getUiType().equals(getThriftCxxTypeMeta(type));
320        }
321        public String getThriftClientNamespace() {
322                return "::"+CxxHelper.cxxNamespace(thriftClientPackage);
323        }
324
325        public Set<CxxTypeMeta> getContainerTypes() {
326                return containerTypes;
327        }
328        public DeriveMethod getDeriveMethodAnnotation(Method method) {
329                try {
330                        return AnnotationUtils.getDeriveMethodAnnotation(method, serviceInfo);
331                } catch (InvalidNameException e) {
332                        throw new RuntimeException(e);
333                } catch (InvalidAnnotationDefineException e) {
334                        throw new RuntimeException(e);
335                }
336        }
337        /**
338         * 返回 {@link DeriveMethod} 注释指定的方法名后缀,没有则返回{@code null}
339         * @param method
340         * @return
341         */
342        public String methodSuffix(Method method){
343                DeriveMethod deriveMethod = getDeriveMethodAnnotation(method);
344                if(deriveMethod !=null ){
345                        String[] suffixs = deriveMethod.methodSuffix();
346                        return suffixs != null && suffixs.length>0 ? suffixs[0]:"";
347                }
348                return "";
349        }
350}