| /*
|
| * Copyright 1999-2011 Alibaba Group.
|
| *
|
| * Licensed under the Apache License, Version 2.0 (the "License");
|
| * you may not use this file except in compliance with the License.
|
| * You may obtain a copy of the License at
|
| *
|
| * http://www.apache.org/licenses/LICENSE-2.0
|
| *
|
| * Unless required by applicable law or agreed to in writing, software
|
| * distributed under the License is distributed on an "AS IS" BASIS,
|
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| * See the License for the specific language governing permissions and
|
| * limitations under the License.
|
| */
|
| package com.alibaba.dubbo.config.spring.schema; |
| |
| import java.lang.reflect.Method;
|
| import java.lang.reflect.Modifier;
|
| import java.util.Date;
|
| import java.util.regex.Pattern;
|
|
|
| import org.springframework.beans.PropertyValue;
|
| 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.config.TypedStringValue;
|
| import org.springframework.beans.factory.support.ManagedList;
|
| import org.springframework.beans.factory.support.ManagedMap;
|
| 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.alibaba.dubbo.common.Constants;
|
| import com.alibaba.dubbo.common.ExtensionLoader;
|
| import com.alibaba.dubbo.common.logger.Logger;
|
| import com.alibaba.dubbo.common.logger.LoggerFactory;
|
| import com.alibaba.dubbo.config.ArgumentConfig;
|
| import com.alibaba.dubbo.config.MethodConfig;
|
| import com.alibaba.dubbo.config.MonitorConfig;
|
| import com.alibaba.dubbo.config.ProtocolConfig;
|
| import com.alibaba.dubbo.config.RegistryConfig;
|
| import com.alibaba.dubbo.rpc.Protocol;
|
| |
| /** |
| * AbstractBeanDefinitionParser |
| * |
| * @author william.liangf |
| */ |
| public class DubboBeanDefinitionParser implements BeanDefinitionParser { |
| |
| private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class); |
| |
| private final Class<?> beanClass; |
| |
| private final boolean required; |
| |
| public DubboBeanDefinitionParser(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.setBeanClass(beanClass); |
| beanDefinition.setLazyInit(false); |
| String id = element.getAttribute("id"); |
| if ((id == null || id.length() == 0) && required) { |
| String generatedBeanName = element.getAttribute("name"); |
| if (generatedBeanName == null || generatedBeanName.length() == 0) {
|
| if (ProtocolConfig.class.equals(beanClass)) {
|
| generatedBeanName = "dubbo";
|
| } else { |
| generatedBeanName = element.getAttribute("interface");
|
| }
|
| } |
| if (generatedBeanName == null || generatedBeanName.length() == 0) { |
| generatedBeanName = beanClass.getName(); |
| } |
| id = generatedBeanName; |
| int counter = 2; |
| while(parserContext.getRegistry().containsBeanDefinition(id)) { |
| id = generatedBeanName + (counter ++); |
| } |
| } |
| if (id != null && id.length() > 0) { |
| if (parserContext.getRegistry().containsBeanDefinition(id)) { |
| throw new IllegalStateException("Duplicate spring bean id " + id); |
| } |
| parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); |
| } |
| if (ProtocolConfig.class.equals(beanClass)) { |
| for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { |
| BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); |
| PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol"); |
| if (property != null) { |
| Object value = property.getValue(); |
| if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) { |
| definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id)); |
| } |
| } |
| } |
| } |
| for (Method setter : beanClass.getMethods()) { |
| String name = setter.getName(); |
| if (name.length() > 3 && name.startsWith("set") |
| && Modifier.isPublic(setter.getModifiers()) |
| && setter.getParameterTypes().length == 1) { |
| Class<?> type = setter.getParameterTypes()[0]; |
| String property = name.substring(3, 4).toLowerCase() + name.substring(4); |
| Method getter = null; |
| try { |
| getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]); |
| } catch (NoSuchMethodException e) { |
| try { |
| getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]); |
| } catch (NoSuchMethodException e2) { |
| } |
| } |
| if (getter == null |
| || ! Modifier.isPublic(getter.getModifiers()) |
| || ! type.equals(getter.getReturnType())) { |
| continue; |
| } |
| if ("parameters".equals(property)) { |
| parseParameters(element.getChildNodes(), beanDefinition); |
| } else if ("methods".equals(property)) { |
| parseMethods(id, element.getChildNodes(), beanDefinition, parserContext); |
| } else if ("arguments".equals(property)) { |
| parseArguments(id, element.getChildNodes(), beanDefinition, parserContext); |
| } else { |
| String value = element.getAttribute(property); |
| if (value != null) { |
| value = value.trim(); |
| if (value.length() > 0) { |
| if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) { |
| RegistryConfig registryConfig = new RegistryConfig(); |
| registryConfig.setAddress(RegistryConfig.NO_AVAILABLE); |
| beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig); |
| } else if ("registry".equals(property) && value.indexOf(',') != -1) { |
| parseMultiRef("registries", value, beanDefinition, parserContext); |
| } else if ("provider".equals(property) && value.indexOf(',') != -1) { |
| parseMultiRef("providers", value, beanDefinition, parserContext); |
| } else if ("protocol".equals(property) && value.indexOf(',') != -1) { |
| parseMultiRef("protocols", value, beanDefinition, parserContext); |
| } else { |
| Object reference; |
| if (isPrimitive(type)) { |
| if ("async".equals(property) && "false".equals(value) |
| || "timeout".equals(property) && "0".equals(value) |
| || "delay".equals(property) && "0".equals(value) |
| || "version".equals(property) && "0.0.0".equals(value) |
| || "stat".equals(property) && "-1".equals(value) |
| || "reliable".equals(property) && "false".equals(value)) { |
| // 兼容旧版本xsd中的default值 |
| value = null; |
| } |
| reference = value; |
| if (! "id".equals(property) |
| && ! "name".equals(property) |
| && ! "interface".equals(property)) { |
| String sysProperty = element.getPrefix() + "." + element.getLocalName() + "." + id + "." + property; |
| String sysValue = System.getProperty(sysProperty); |
| if (sysValue != null && sysValue.trim().length() > 0) { |
| reference = sysValue.trim(); |
| } else { |
| sysProperty = element.getPrefix() + "." + element.getLocalName() + "." + property; |
| sysValue = System.getProperty(sysProperty); |
| if (sysValue != null && sysValue.trim().length() > 0) { |
| reference = sysValue.trim(); |
| } |
| } |
| } |
| } else if ("protocol".equals(property) |
| && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value) |
| && (! parserContext.getRegistry().containsBeanDefinition(value) |
| || ! ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) { |
| if ("dubbo:provider".equals(element.getTagName())) { |
| logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />"); |
| } |
| // 兼容旧版本配置 |
| ProtocolConfig protocol = new ProtocolConfig(); |
| protocol.setName(value); |
| reference = protocol; |
| } else if ("monitor".equals(property)
|
| && (! parserContext.getRegistry().containsBeanDefinition(value)
|
| || ! MonitorConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
|
| // 兼容旧版本配置
|
| reference = convertMonitor(value);
|
| } else if ("onreturn".equals(property)) { |
| int index = value.lastIndexOf("."); |
| String returnRef = value.substring(0, index); |
| String returnMethod = value.substring(index + 1); |
| reference = new RuntimeBeanReference(returnRef); |
| beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod); |
| } else if ("onthrow".equals(property)) { |
| int index = value.lastIndexOf("."); |
| String throwRef = value.substring(0, index); |
| String throwMethod = value.substring(index + 1); |
| reference = new RuntimeBeanReference(throwRef); |
| beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
|
| } else { |
| if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) { |
| BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value); |
| if (! refBean.isSingleton()) { |
| throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value+ "\" scope=\"singleton\" ...>"); |
| } |
| } |
| reference = new RuntimeBeanReference(value); |
| } |
| beanDefinition.getPropertyValues().addPropertyValue(property, reference); |
| } |
| } |
| } |
| } |
| } |
| } |
| return beanDefinition; |
| }
|
|
|
| private static final Pattern GROUP_AND_VERION = Pattern.compile("^[\\-.0-9_a-zA-Z]+(\\:[\\-.0-9_a-zA-Z]+)?$");
|
|
|
| protected static MonitorConfig convertMonitor(String monitor) {
|
| if (monitor == null || monitor.length() == 0) {
|
| return null;
|
| }
|
| if (GROUP_AND_VERION.matcher(monitor).matches()) {
|
| String group;
|
| String version;
|
| int i = monitor.indexOf(':');
|
| if (i > 0) {
|
| group = monitor.substring(0, i);
|
| version = monitor.substring(i + 1);
|
| } else {
|
| group = monitor;
|
| version = null;
|
| }
|
| MonitorConfig monitorConfig = new MonitorConfig();
|
| monitorConfig.setGroup(group);
|
| monitorConfig.setVersion(version);
|
| return monitorConfig;
|
| }
|
| return null;
|
| }
|
| |
| private static boolean isPrimitive(Class<?> cls) { |
| return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class |
| || cls == Character.class || cls == Short.class || cls == Integer.class |
| || cls == Long.class || cls == Float.class || cls == Double.class |
| || cls == String.class || cls == Date.class || cls == Class.class; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition, |
| ParserContext parserContext) { |
| String[] values = value.split("\\s*[,]+\\s*"); |
| ManagedList list = null; |
| for (int i = 0; i < values.length; i++) { |
| String v = values[i]; |
| if (v != null && v.length() > 0) { |
| if (list == null) { |
| list = new ManagedList(); |
| } |
| list.add(new RuntimeBeanReference(v)); |
| } |
| } |
| beanDefinition.getPropertyValues().addPropertyValue(property, list); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static void parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) { |
| if (nodeList != null && nodeList.getLength() > 0) { |
| ManagedMap parameters = null; |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Element) { |
| if ("parameter".equals(node.getNodeName()) |
| || "parameter".equals(node.getLocalName())) { |
| if (parameters == null) { |
| parameters = new ManagedMap(); |
| } |
| String key = ((Element) node).getAttribute("key"); |
| String value = ((Element) node).getAttribute("value"); |
| boolean hide = "true".equals(((Element) node).getAttribute("hide")); |
| if (hide) { |
| key = Constants.HIDE_KEY_PREFIX + key; |
| } |
| parameters.put(key, new TypedStringValue(value, String.class)); |
| } |
| } |
| } |
| if (parameters != null) { |
| beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters); |
| } |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| 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("<dubbo:method> name attribute == null"); |
| } |
| if (methods == null) { |
| methods = new ManagedList(); |
| } |
| BeanDefinition methodBeanDefinition = parse(((Element) node), |
| parserContext, MethodConfig.class, false); |
| String name = id + "." + methodName; |
| BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder( |
| methodBeanDefinition, name); |
| methods.add(methodBeanDefinitionHolder); |
| } |
| } |
| } |
| if (methods != null) { |
| beanDefinition.getPropertyValues().addPropertyValue("methods", methods); |
| } |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static void parseArguments(String id, NodeList nodeList, RootBeanDefinition beanDefinition, |
| ParserContext parserContext) { |
| if (nodeList != null && nodeList.getLength() > 0) { |
| ManagedList arguments = null; |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node node = nodeList.item(i); |
| if (node instanceof Element) { |
| Element element = (Element) node; |
| if ("argument".equals(node.getNodeName()) || "argument".equals(node.getLocalName())) { |
| String argumentIndex = element.getAttribute("index"); |
| if (arguments == null) { |
| arguments = new ManagedList(); |
| } |
| BeanDefinition argumentBeanDefinition = parse(((Element) node), |
| parserContext, ArgumentConfig.class, false); |
| String name = id + "." + argumentIndex; |
| BeanDefinitionHolder argumentBeanDefinitionHolder = new BeanDefinitionHolder( |
| argumentBeanDefinition, name); |
| arguments.add(argumentBeanDefinitionHolder); |
| } |
| } |
| } |
| if (arguments != null) { |
| beanDefinition.getPropertyValues().addPropertyValue("arguments", arguments); |
| } |
| } |
| } |
| |
| } |