/** * Dianping.com Inc. * Copyright (c) 2003-2013 All Rights Reserved. */ package com.dianping.pigeon.remoting.invoker.config.spring; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.lang.StringUtils; import com.dianping.pigeon.log.Logger; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.dianping.pigeon.config.ConfigManager; import com.dianping.pigeon.config.ConfigManagerLoader; import com.dianping.pigeon.log.LoggerLoader; import com.dianping.pigeon.remoting.invoker.InvokerBootStrap; import com.dianping.pigeon.remoting.invoker.config.InvokerMethodConfig; import com.dianping.pigeon.util.ClassUtils; public class ReferenceBeanDefinitionParser implements BeanDefinitionParser { /** Default placeholder prefix: "${" */ public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"; /** Default placeholder suffix: "}" */ public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"; private static final Logger logger = LoggerLoader.getLogger(ReferenceBeanDefinitionParser.class); private final Class<?> beanClass; private final boolean required; private static AtomicInteger idCounter = new AtomicInteger(); private static ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private static boolean checkRefExists = configManager.getBooleanValue("pigeon.config.spring.checkrefexists", false); public ReferenceBeanDefinitionParser(Class<?> beanClass, boolean required) { this.beanClass = beanClass; this.required = required; } public BeanDefinition parse(Element element, ParserContext parserContext) { return parse(element, parserContext, beanClass, required); } private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setLazyInit(false); String id = element.getAttribute("id"); if (StringUtils.isBlank(id)) { id = "pigeonRef_" + idCounter.incrementAndGet(); } beanDefinition.setBeanClass(ReferenceBean.class); beanDefinition.setInitMethodName("init"); InvokerBootStrap.startup(); MutablePropertyValues properties = beanDefinition.getPropertyValues(); if (element.hasAttribute("interface")) { properties.addPropertyValue("interfaceName", resolveReference(element, "interface")); } if (element.hasAttribute("url")) { properties.addPropertyValue("url", resolveReference(element, "url")); } else if (element.hasAttribute("interface")) { properties.addPropertyValue("url", resolveReference(element, "interface")); } if (element.hasAttribute("serialize")) { properties.addPropertyValue("serialize", resolveReference(element, "serialize")); } if (element.hasAttribute("protocol")) { properties.addPropertyValue("protocol", resolveReference(element, "protocol")); } if (element.hasAttribute("callType")) { properties.addPropertyValue("callType", resolveReference(element, "callType")); } if (element.hasAttribute("timeout")) { properties.addPropertyValue("timeout", resolveReference(element, "timeout")); } if (element.hasAttribute("loadBalance")) { properties.addPropertyValue("loadBalance", resolveReference(element, "loadBalance")); } if (element.hasAttribute("loadBalanceClass")) { String clazz = resolveReference(element, "loadBalanceClass"); if (StringUtils.isNotBlank(clazz)) { try { Class<?> cl = ClassUtils.loadClass(clazz); properties.addPropertyValue("loadBalanceClass", cl); } catch (ClassNotFoundException e) { logger.warn("invalid loadBalanceClass:" + clazz + ", caused by " + e.getMessage()); } } } if (element.hasAttribute("cluster")) { properties.addPropertyValue("cluster", resolveReference(element, "cluster")); } if (element.hasAttribute("retries")) { properties.addPropertyValue("retries", resolveReference(element, "retries")); } if (element.hasAttribute("timeoutRetry")) { properties.addPropertyValue("timeoutRetry", resolveReference(element, "timeoutRetry")); } if (element.hasAttribute("version")) { properties.addPropertyValue("version", resolveReference(element, "version")); } if (element.hasAttribute("vip")) { properties.addPropertyValue("vip", resolveReference(element, "vip")); } if (element.hasAttribute("secret")) { properties.addPropertyValue("secret", resolveReference(element, "secret")); } String callback = element.getAttribute("callback"); if (StringUtils.isNotEmpty(callback)) { if (checkRefExists && !parserContext.getRegistry().containsBeanDefinition(callback)) { throw new IllegalStateException("callback reference must have a reference to callback bean"); } properties.addPropertyValue("callback", new RuntimeBeanReference(callback)); } if (element.hasChildNodes()) { parseMethods(id, element.getChildNodes(), beanDefinition, parserContext); } parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); return beanDefinition; } private static BeanDefinition parseMethod(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setLazyInit(false); String id = element.getAttribute("id"); if (StringUtils.isBlank(id)) { id = "pigeonService_" + idCounter.incrementAndGet(); } beanDefinition.setBeanClass(beanClass); MutablePropertyValues properties = beanDefinition.getPropertyValues(); if (element.hasAttribute("name")) { properties.addPropertyValue("name", resolveReference(element, "name")); } if (element.hasAttribute("timeout")) { properties.addPropertyValue("timeout", resolveReference(element, "timeout")); } if (element.hasAttribute("retries")) { properties.addPropertyValue("retries", resolveReference(element, "retries")); } if (element.hasAttribute("actives")) { properties.addPropertyValue("actives", resolveReference(element, "actives")); } if (element.hasAttribute("callType")) { properties.addPropertyValue("callType", resolveReference(element, "callType")); } parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); return beanDefinition; } private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext) { if (nodeList != null && nodeList.getLength() > 0) { ManagedList methods = null; for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element) { Element element = (Element) node; if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) { String methodName = element.getAttribute("name"); if (methodName == null || methodName.length() == 0) { throw new IllegalStateException("<pigeon:method> name attribute == null"); } if (methods == null) { methods = new ManagedList(); } BeanDefinition methodBeanDefinition = parseMethod(((Element) node), parserContext, InvokerMethodConfig.class, false); String name = id + "." + methodName; BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder( methodBeanDefinition, name); methods.add(methodBeanDefinitionHolder); } } } if (methods != null) { beanDefinition.getPropertyValues().addPropertyValue("methods", methods); } } } private static String resolveReference(Element element, String attribute) { String value = element.getAttribute(attribute); if (value.startsWith(DEFAULT_PLACEHOLDER_PREFIX) && value.endsWith(DEFAULT_PLACEHOLDER_SUFFIX)) { String valueInCache = configManager.getStringValue(value.substring(2, value.length() - 1)); if (valueInCache == null) { throw new IllegalStateException("undefined config property:" + element.getAttribute(attribute)); } else { value = valueInCache; } } return value; } }