package io.github.saluki.monitor.rest; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.type.StandardMethodMetadata; import org.springframework.util.ClassUtils; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gson.Gson; import io.github.saluki.boot.SalukiReference; import io.github.saluki.boot.SalukiService; import io.github.saluki.boot.autoconfigure.GrpcProperties; import io.github.saluki.boot.common.GrpcAop; import io.github.saluki.monitor.dao.domain.GrpcServiceTestModel; import io.github.saluki.monitor.jaket.Jaket; import io.github.saluki.monitor.jaket.model.GenericInvokeMetadata; import io.github.saluki.monitor.jaket.model.MetadataType; import io.github.saluki.monitor.jaket.model.MethodDefinition; import io.github.saluki.monitor.jaket.model.ServiceDefinition; import io.github.saluki.monitor.jaket.util.GenericInvokeUtils; import io.github.saluki.common.RpcContext; import io.github.saluki.grpc.service.GenericService; import io.github.saluki.utils.ReflectUtils; @RestController @RequestMapping("service") public class TestController { private final Gson gson = new Gson(); @Autowired private GrpcProperties prop; @Autowired private AbstractApplicationContext applicationContext; @SalukiReference(group = "default", version = "1.0.0") private GenericService genricService; @RequestMapping(value = "getAllMethod", method = RequestMethod.GET) public List<MethodDefinition> getAllMethod( @RequestParam(value = "service", required = true) String service) throws ClassNotFoundException { try { Class<?> clazz = ReflectUtils.name2class(service); ServiceDefinition sd = Jaket.build(clazz); return sd.getMethods(); } catch (ClassNotFoundException e) { throw e; } } @RequestMapping(value = "getAllService", method = RequestMethod.GET) public List<Map<String, Object>> getAllService() throws Exception { List<Map<String, Object>> services = Lists.newArrayList(); try { Collection<Object> instances = getTypedBeansWithAnnotation(SalukiService.class); for (Object instance : instances) { Object target = GrpcAop.getTarget(instance); Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(target.getClass()); Class<?> clzz = interfaces[0]; Map<String, Object> serviceMap = Maps.newHashMap(); serviceMap.put("simpleName", clzz.getSimpleName()); serviceMap.put("name", clzz.getName()); ServiceDefinition sd = Jaket.build(clzz); List<MethodDefinition> methodDefines = sd.getMethods(); List<String> functions = Lists.newArrayList(); for (MethodDefinition methodDefine : methodDefines) { functions.add(methodDefine.getName()); } serviceMap.put("functions", functions); services.add(serviceMap); } return services; } catch (Exception e) { throw e; } } @RequestMapping(value = "getMethod", method = RequestMethod.GET) public GenericInvokeMetadata getMethod( @RequestParam(value = "service", required = true) String service, @RequestParam(value = "method", required = true) String method) throws ClassNotFoundException { try { Class<?> clazz = ReflectUtils.name2class(service); ServiceDefinition serviceMeta = Jaket.build(clazz); if (!method.contains("~")) { for (MethodDefinition methodDef : serviceMeta.getMethods()) { if (methodDef.getName().equals(method)) { method = method + "~" + methodDef.getParameterTypes()[0]; break; } } } GenericInvokeMetadata meta = GenericInvokeUtils.getGenericInvokeMetadata(serviceMeta, method, MetadataType.DEFAULT_VALUE); return meta; } catch (ClassNotFoundException e) { throw e; } } @RequestMapping(value = "testLocal", method = RequestMethod.POST) public Object testLocalService( @RequestParam(value = "routerRule", required = true) String routerRule, @RequestBody GrpcServiceTestModel model) throws ClassNotFoundException { try { Class<?> requestClass = ReflectUtils.name2class(model.getParameterType()); Object request = gson.fromJson(model.getParameter(), requestClass); Object[] args = new Object[] {request}; if (StringUtils.isNotBlank(routerRule)) { RpcContext.getContext().setAttachment("routerRule", routerRule); } else { RpcContext.getContext().removeAttachment("routerRule"); } Object reply = genricService.$invoke(model.getService(), getAnnotation(model.getService()).getLeft(), getAnnotation(model.getService()).getRight(), model.getMethod(), args); return reply; } catch (ClassNotFoundException e) { throw e; } } @RequestMapping(value = "test", method = RequestMethod.POST) public Object testService(@RequestBody GrpcServiceTestModel model) throws ClassNotFoundException { try { Class<?> requestClass = ReflectUtils.name2class(model.getParameterType()); Object request = gson.fromJson(model.getParameter(), requestClass); Object[] args = new Object[] {request}; Object reply = genricService.$invoke(model.getService(), getAnnotation(model.getService()).getLeft(), getAnnotation(model.getService()).getRight(), model.getMethod(), args); return reply; } catch (ClassNotFoundException e) { throw e; } } private Pair<String, String> getAnnotation(String className) throws ClassNotFoundException { Class<?> beanType = ReflectUtils.name2class(className); Map<String, ?> beanMap = applicationContext.getBeansOfType(beanType); String group = null; String version = null; for (Map.Entry<String, ?> entry : beanMap.entrySet()) { Object obj = entry.getValue(); SalukiService salukiAnnotation = obj.getClass().getAnnotation(SalukiService.class); group = salukiAnnotation.group(); version = salukiAnnotation.version(); break; } if (StringUtils.isBlank(group) || StringUtils.isBlank(version)) { group = prop.getGroup(); version = prop.getVersion(); } return new ImmutablePair<String, String>(group, version); } private Collection<Object> getTypedBeansWithAnnotation(Class<? extends Annotation> annotationType) throws Exception { return Stream.of(applicationContext.getBeanNamesForAnnotation(annotationType)).filter(name -> { BeanDefinition beanDefinition = applicationContext.getBeanFactory().getBeanDefinition(name); if (beanDefinition.getSource() instanceof StandardMethodMetadata) { StandardMethodMetadata metadata = (StandardMethodMetadata) beanDefinition.getSource(); return metadata.isAnnotated(annotationType.getName()); } return null != applicationContext.getBeanFactory().findAnnotationOnBean(name, annotationType); }).map(name -> applicationContext.getBeanFactory().getBean(name)).collect(Collectors.toList()); } }