col, Class primitiveArrayType) {
+ if (primitiveArrayType == Integer.TYPE) {
+ return toIntArray(col);
+ } else if (primitiveArrayType == Long.TYPE) {
+ return toLongArray(col);
+ } else if (primitiveArrayType == Double.TYPE) {
+ return toDoubleArray(col);
+ } else if (primitiveArrayType == Float.TYPE) {
+ return toFloatArray(col);
+ } else if (primitiveArrayType == Boolean.TYPE) {
+ return toBooleanArray(col);
+ } else if (primitiveArrayType == Byte.TYPE) {
+ return toByteArray(col);
+ } else if (primitiveArrayType == Character.TYPE) {
+ return toCharArray(col);
+ } else if (primitiveArrayType == Short.TYPE) {
+ return toShortArray(col);
+ }
+ return null;
+ }
+
+ public static int[] toIntArray(Collection col) {
+ int size = col.size();
+ int[] ar = new int[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Integer) it.next();
+ }
+ return ar;
+ }
+
+ public static long[] toLongArray(Collection col) {
+ int size = col.size();
+ long[] ar = new long[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Long) it.next();
+ }
+ return ar;
+ }
+
+ public static double[] toDoubleArray(Collection col) {
+ int size = col.size();
+ double[] ar = new double[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Double) it.next();
+ }
+ return ar;
+ }
+
+ public static float[] toFloatArray(Collection col) {
+ int size = col.size();
+ float[] ar = new float[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Float) it.next();
+ }
+ return ar;
+ }
+
+ public static boolean[] toBooleanArray(Collection col) {
+ int size = col.size();
+ boolean[] ar = new boolean[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Boolean) it.next();
+ }
+ return ar;
+ }
+
+ public static short[] toShortArray(Collection col) {
+ int size = col.size();
+ short[] ar = new short[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Short) it.next();
+ }
+ return ar;
+ }
+
+ public static byte[] toByteArray(Collection col) {
+ int size = col.size();
+ byte[] ar = new byte[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Byte) it.next();
+ }
+ return ar;
+ }
+
+ public static char[] toCharArray(Collection col) {
+ int size = col.size();
+ char[] ar = new char[size];
+ Iterator it = col.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ ar[i++] = (Character) it.next();
+ }
+ return ar;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/Resource.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/Resource.java
new file mode 100644
index 000000000..27115fd0e
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/Resource.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+/**
+ * A resource that can be retrieved using the class loader.
+ *
+ * This is wrapping an URL as returned by the class loader.
+ *
+ * The URL class cannot be used directly because it already has a factory associated to it that constructs the URL using
+ * its constructor.
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class Resource {
+
+ private final URL url;
+
+ public Resource(URL url) {
+ this.url = url;
+ }
+
+ public Resource(Context ctx, String path) {
+ url = ctx.getResource(path);
+ }
+
+ public URL toURL() {
+ return url;
+ }
+
+ public URI toURI() throws URISyntaxException {
+ return url != null ? url.toURI() : null;
+ }
+
+ public File toFile() throws URISyntaxException {
+ return url != null ? new File(url.toURI()) : null;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedContent.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedContent.java
new file mode 100755
index 000000000..c32c5017d
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedContent.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import com.alipay.sofa.common.xmap.annotation.XContent;
+import com.sun.org.apache.xml.internal.serialize.OutputFormat;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.ranges.DocumentRange;
+import org.w3c.dom.ranges.Range;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XAnnotatedContent extends XAnnotatedMember {
+
+ private static final OutputFormat DEFAULT_FORMAT = new OutputFormat();
+
+ static {
+ DEFAULT_FORMAT.setOmitXMLDeclaration(true);
+ DEFAULT_FORMAT.setIndenting(true);
+ DEFAULT_FORMAT.setMethod("xml");
+ DEFAULT_FORMAT.setEncoding("UTF-8");
+ }
+
+ public XAnnotatedContent(XMap xmap, XSetter setter, XGetter getter, XContent anno) {
+ super(xmap, setter, getter);
+ this.path = new Path(anno.value());
+ this.type = setter.getType();
+ this.valueFactory = xmap.getValueFactory(this.type);
+ this.xao = xmap.register(this.type);
+ }
+
+ @Override
+ protected Object getValue(Context ctx, Element base) throws IOException {
+ Element el = (Element) DOMHelper.getElementNode(base, path);
+ if (el == null) {
+ return null;
+ }
+ el.normalize();
+ Node node = el.getFirstChild();
+ if (node == null) {
+ return "";
+ }
+ Range range = ((DocumentRange) el.getOwnerDocument()).createRange();
+ range.setStartBefore(node);
+ range.setEndAfter(el.getLastChild());
+ DocumentFragment fragment = range.cloneContents();
+ boolean asDOM = setter.getType() == DocumentFragment.class;
+ return asDOM ? fragment : DOMSerializer.toString(fragment, DEFAULT_FORMAT);
+ }
+
+ @Override
+ public void decode(Object instance, Node base, Document document, List filters)
+ throws Exception {
+ // do nothing
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedList.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedList.java
new file mode 100644
index 000000000..0ca6bd053
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedList.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import com.alipay.sofa.common.xmap.annotation.XNodeList;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XAnnotatedList extends XAnnotatedMember {
+
+ protected static final ElementVisitor elementListVisitor = new ElementVisitor();
+ protected static final ElementValueVisitor elementVisitor = new ElementValueVisitor();
+ protected static final AttributeValueVisitor attributeVisitor = new AttributeValueVisitor();
+
+ // indicates the type of the collection components
+ public Class> componentType;
+
+ protected XAnnotatedList(XMap xmap, XSetter setter, XGetter getter) {
+ super(xmap, setter, getter);
+ }
+
+ public XAnnotatedList(XMap xmap, XSetter setter, XGetter getter, XNodeList anno) {
+ super(xmap, setter, getter);
+ path = new Path(anno.value());
+ trim = anno.trim();
+ type = anno.type();
+ cdata = anno.cdata();
+ componentType = anno.componentType();
+ valueFactory = xmap.getValueFactory(componentType);
+ xao = xmap.register(componentType);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Object getValue(Context ctx, Element base) throws Exception {
+ ArrayList values = new ArrayList();
+ if (xao != null) {
+ DOMHelper.visitNodes(ctx, this, base, path, elementListVisitor, values);
+ } else {
+ if (path.attribute != null) {
+ // attribute list
+ DOMHelper.visitNodes(ctx, this, base, path, attributeVisitor, values);
+ } else {
+ // element list
+ DOMHelper.visitNodes(ctx, this, base, path, elementVisitor, values);
+ }
+ }
+
+ if (type != ArrayList.class) {
+ if (type.isArray()) {
+ if (componentType.isPrimitive()) {
+ // primitive arrays cannot be casted to Object[]
+ return PrimitiveArrays.toPrimitiveArray(values, componentType);
+ } else {
+ return values
+ .toArray((Object[]) Array.newInstance(componentType, values.size()));
+ }
+ } else {
+ Collection col = (Collection) type.newInstance();
+ col.addAll(values);
+ return col;
+ }
+ }
+
+ return values;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void decode(Object instance, Node base, Document document, List filters)
+ throws Exception {
+ if (!isFilter(filters)) {
+ return;
+ }
+
+ Collection col = null;
+ if (Collection.class.isAssignableFrom(type)) {
+ col = (Collection) getter.getValue(instance);
+ } else {
+ if (type.isArray()) {
+ col = new ArrayList();
+
+ Object obj = getter.getValue(instance);
+
+ int length = Array.getLength(obj);
+
+ for (int i = 0; i < length; i++) {
+ col.add(Array.get(obj, i));
+ }
+ } else {
+ throw new Exception("@XNodeList " + base.getNodeName()
+ + " 'type' only support Collection ande Array type");
+ }
+ }
+
+ Node node = base;
+ int len = path.segments.length - 1;
+ for (int i = 0; i < len; i++) {
+ Node n = DOMHelper.getElementNode(node, path.segments[i]);
+ if (n == null) {
+ Element element = document.createElement(path.segments[i]);
+ node = node.appendChild(element);
+ } else {
+ node = n;
+ }
+ }
+
+ String name = path.segments[len];
+
+ Node lastParentNode = node;
+
+ for (Object object : col) {
+
+ Element element = document.createElement(name);
+ node = lastParentNode.appendChild(element);
+
+ if (xao != null) {
+ xao.decode(object, node, document, filters);
+ } else {
+ String value = object == null ? "" : object.toString();
+
+ if (path.attribute != null && path.attribute.length() > 0) {
+ Attr attr = document.createAttribute(path.attribute);
+ attr.setNodeValue(value);
+
+ ((Element) node).setAttributeNode(attr);
+ } else {
+ if (cdata) {
+ CDATASection cdataSection = document.createCDATASection(value);
+ node.appendChild(cdataSection);
+ } else {
+ node.setTextContent(value);
+ }
+ }
+ }
+ }
+
+ }
+}
+
+/**
+ * Element
NodeVisitor
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+class ElementVisitor extends DOMHelper.NodeVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection result) {
+ try {
+ result.add(xam.xao.newInstance(ctx, (Element) node));
+ } catch (Throwable e) {
+ SofaLogger.error("visitNode error", e);
+ }
+ }
+}
+
+/**
+ * ElementValue
NodeVisitor
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+class ElementValueVisitor extends DOMHelper.NodeVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection result) {
+ String val = node.getTextContent();
+ if (xam.trim) {
+ val = val.trim();
+ }
+ if (xam.valueFactory != null) {
+ result.add(xam.valueFactory.getValue(ctx, val));
+ } else {
+ // TODO: log warning?
+ result.add(val);
+ }
+ }
+}
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+class AttributeValueVisitor extends DOMHelper.NodeVisitor {
+ /**
+ *
+ * @param ctx
+ * @param xam
+ * @param node
+ * @param result
+ */
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection result) {
+ String val = node.getNodeValue();
+ if (xam.valueFactory != null) {
+ result.add(xam.valueFactory.getValue(ctx, val));
+ } else {
+ // TODO: log warning?
+ result.add(val);
+ }
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedMap.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedMap.java
new file mode 100644
index 000000000..f4c67d001
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedMap.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import com.alipay.sofa.common.xmap.annotation.XNodeMap;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XAnnotatedMap extends XAnnotatedList {
+
+ protected static final ElementMapVisitor elementMapVisitor = new ElementMapVisitor();
+ protected static final ElementValueMapVisitor elementVisitor = new ElementValueMapVisitor();
+ protected static final AttributeValueMapVisitor attributeVisitor = new AttributeValueMapVisitor();
+
+ protected Path key;
+
+ public XAnnotatedMap(XMap xmap, XSetter setter, XGetter getter, XNodeMap anno) {
+ super(xmap, setter, getter);
+ if (anno != null) {
+ path = new Path(anno.value());
+ trim = anno.trim();
+ key = new Path(anno.key());
+ type = anno.type();
+ cdata = anno.cdata();
+ componentType = anno.componentType();
+ valueFactory = xmap.getValueFactory(componentType);
+ xao = xmap.register(componentType);
+ }
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Object getValue(Context ctx, Element base) throws IllegalAccessException,
+ InstantiationException {
+ Map values = (Map) type.newInstance();
+ if (xao != null) {
+ DOMHelper.visitMapNodes(ctx, this, base, path, elementMapVisitor, values);
+ } else {
+ if (path.attribute != null) {
+ // attribute list
+ DOMHelper.visitMapNodes(ctx, this, base, path, attributeVisitor, values);
+ } else {
+ // element list
+ DOMHelper.visitMapNodes(ctx, this, base, path, elementVisitor, values);
+ }
+ }
+
+ return values;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void decode(Object instance, Node base, Document document, List filters)
+ throws Exception {
+ if (!isFilter(filters)) {
+ return;
+ }
+
+ Map values = (Map) getter.getValue(instance);
+
+ Node node = base;
+ int len = path.segments.length - 1;
+ for (int i = 0; i < len; i++) {
+ Node n = DOMHelper.getElementNode(node, path.segments[i]);
+ if (n == null) {
+ Element element = document.createElement(path.segments[i]);
+ node = node.appendChild(element);
+ } else {
+ node = n;
+ }
+ }
+
+ String name = path.segments[len];
+
+ Node lastParentNode = node;
+
+ Set entrys = values.entrySet();
+ for (Map.Entry entry : entrys) {
+
+ Element element = document.createElement(name);
+ node = lastParentNode.appendChild(element);
+
+ Object keyObj = entry.getKey();
+ String keyValue = keyObj == null ? "" : keyObj.toString();
+
+ Object object = entry.getValue();
+
+ Attr attrKey = document.createAttribute(key.attribute);
+ attrKey.setNodeValue(keyValue);
+ ((Element) node).setAttributeNode(attrKey);
+
+ if (xao != null) {
+ xao.decode(object, node, document, filters);
+ } else {
+ String value = object == null ? "" : object.toString();
+
+ if (path.attribute != null && path.attribute.length() > 0) {
+ Attr attrValue = document.createAttribute(path.attribute);
+ attrValue.setNodeValue(value);
+
+ ((Element) node).setAttributeNode(attrValue);
+ } else {
+ if (cdata) {
+ CDATASection cdataSection = document.createCDATASection(value);
+ node.appendChild(cdataSection);
+ } else {
+ node.setTextContent(value);
+ }
+ }
+ }
+ }
+ }
+
+}
+
+/**
+ * ElementMap
NodeVisitor
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+class ElementMapVisitor extends DOMHelper.NodeMapVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key,
+ Map result) {
+ try {
+ result.put(key, xam.xao.newInstance(ctx, (Element) node));
+ } catch (Exception e) {
+ SofaLogger.error("visitNode error", e);
+ }
+ }
+}
+
+/**
+ * ElementValueMap
NodeVisitor
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+class ElementValueMapVisitor extends DOMHelper.NodeMapVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key,
+ Map result) {
+ String val = node.getTextContent();
+ if (xam.trim) {
+ val = val.trim();
+ }
+ if (xam.valueFactory != null) {
+ result.put(key, xam.valueFactory.getValue(ctx, val));
+ } else {
+ // TODO: log warning?
+ result.put(key, val);
+ }
+ }
+}
+
+/**
+ * AttributeValueMap
NodeVisitor
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+class AttributeValueMapVisitor extends DOMHelper.NodeMapVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key,
+ Map result) {
+ String val = node.getNodeValue();
+ if (xam.valueFactory != null) {
+ result.put(key, xam.valueFactory.getValue(ctx, val));
+ } else {
+ // TODO: log warning?
+ result.put(key, val);
+ }
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedMember.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedMember.java
new file mode 100644
index 000000000..c0163bfc4
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedMember.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import com.alipay.sofa.common.xmap.annotation.XNode;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XAnnotatedMember {
+
+ public final XMap xmap;
+ public final XSetter setter;
+ public final XGetter getter;
+ public Path path;
+ public boolean trim;
+ public boolean cdata;
+
+ // the java type of the described element
+ public Class type;
+ // not null if the described object is an xannotated object
+ public XAnnotatedObject xao;
+ // the value factory used to transform strings in objects compatible
+ // with this member type
+ // In the case of collection types this factory is
+ // used for collection components
+ public XValueFactory valueFactory;
+
+ protected XAnnotatedMember(XMap xmap, XSetter setter, XGetter getter) {
+ this.xmap = xmap;
+ this.setter = setter;
+ this.getter = getter;
+ }
+
+ public XAnnotatedMember(XMap xmap, XSetter setter, XGetter getter, XNode anno) {
+ this.xmap = xmap;
+ this.setter = setter;
+ this.getter = getter;
+ this.path = new Path(anno.value());
+ this.trim = anno.trim();
+ this.cdata = anno.cdata();
+ this.type = setter.getType();
+ this.valueFactory = xmap.getValueFactory(this.type);
+ this.xao = xmap.register(this.type);
+ }
+
+ protected void setValue(Object instance, Object value) throws Exception {
+ setter.setValue(instance, value);
+ }
+
+ public void process(Context ctx, Element element) throws Exception {
+ Object value = getValue(ctx, element);
+ if (value != null) {
+ setValue(ctx.getObject(), value);
+ }
+ }
+
+ public void process(Context ctx, Map map, String keyPrefix) throws Exception {
+ Object value = getValue(ctx, map, keyPrefix);
+ if (value != null) {
+ setValue(ctx.getObject(), value);
+ }
+ }
+
+ public void decode(Object instance, Node base, Document document, List filters)
+ throws Exception {
+ if (!isFilter(filters)) {
+ return;
+ }
+
+ Node node = base;
+
+ int len = path.segments.length;
+ for (int i = 0; i < len; i++) {
+ Node n = DOMHelper.getElementNode(node, path.segments[i]);
+ if (n == null) {
+ Element element = document.createElement(path.segments[i]);
+ node = node.appendChild(element);
+ } else {
+ node = n;
+ }
+ }
+
+ Object object = getter.getValue(instance);
+
+ if (object != null && Element.class.isAssignableFrom(object.getClass())) {
+ return;
+ }
+
+ if (xao != null) {
+ xao.decode(object, node, document, filters);
+ } else {
+ String value = object == null ? "" : object.toString();
+
+ if (path.attribute != null && path.attribute.length() > 0) {
+ Attr attr = document.createAttribute(path.attribute);
+ attr.setNodeValue(value);
+
+ ((Element) node).setAttributeNode(attr);
+ } else {
+ if (cdata) {
+ CDATASection cdataSection = document.createCDATASection(value);
+ node.appendChild(cdataSection);
+ } else {
+ node.setTextContent(value);
+ }
+ }
+ }
+ }
+
+ protected Object getValue(Context ctx, Element base) throws Exception {
+ if (xao != null) {
+ Element el = (Element) DOMHelper.getElementNode(base, path);
+ return el == null ? null : xao.newInstance(ctx, el);
+ }
+ // scalar field
+ if (type == Element.class) {
+ // allow DOM elements as values
+ return base;
+ }
+ String val = DOMHelper.getNodeValue(base, path);
+ if (val != null) {
+ if (trim) {
+ val = val.trim();
+ }
+ return valueFactory.getValue(ctx, val);
+ }
+ return null;
+ }
+
+ protected Object getValue(Context ctx, Map map, String keyPrefix)
+ throws Exception {
+ String key = keyPrefix == null ? path.path : keyPrefix + path.path;
+ Object val = map.get(key);
+ Object result = null;
+
+ if (val == null) {
+ result = null;
+ } else if (val instanceof String) {
+ String str = (String) val;
+ if (str != null) {
+ if (trim) {
+ str = str.trim();
+ }
+ result = valueFactory.getValue(ctx, str);
+ }
+ } else {
+ result = val;
+ }
+
+ return result;
+ }
+
+ protected boolean isFilter(List filters) {
+ boolean filter = false;
+
+ if (filters == null || filters.size() == 0) {
+ filter = true;
+ } else {
+ filter = filters.contains(path.path);
+ }
+
+ return filter;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedObject.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedObject.java
new file mode 100644
index 000000000..218c94d07
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedObject.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XAnnotatedObject {
+
+ public final XMap xmap;
+ public final Class> klass;
+ public final Path path;
+
+ final List members;
+
+ Sorter sorter;
+
+ Sorter deSorter;
+
+ public XAnnotatedObject(XMap xmap, Class> klass, XObject xob) {
+ this.xmap = xmap;
+ this.klass = klass;
+ path = new Path(xob.value());
+ members = new ArrayList<>();
+ String[] order = xob.order();
+ if (order.length > 0) {
+ sorter = new Sorter(order);
+ }
+ }
+
+ public void addMember(XAnnotatedMember member) {
+ members.add(member);
+ }
+
+ public Path getPath() {
+ return path;
+ }
+
+ public Object newInstance(Context ctx, Element element) throws Exception {
+ Object ob = klass.newInstance();
+ ctx.push(ob);
+
+ if (sorter != null) {
+ Collections.sort(members, sorter);
+ deSorter = sorter;
+ sorter = null; // sort only once
+ }
+
+ // set annotated members
+ for (XAnnotatedMember member : members) {
+ member.process(ctx, element);
+ }
+
+ return ctx.pop();
+ }
+
+ public Object newInstance(Context ctx, Map map, String keyPrefix)
+ throws Exception {
+ Object ob = klass.newInstance();
+ ctx.push(ob);
+
+ // set annotated members
+ for (XAnnotatedMember member : members) {
+ member.process(ctx, map, keyPrefix);
+ }
+
+ return ctx.pop();
+ }
+
+ public void decode(Object instance, Node base, Document document, List filters)
+ throws Exception {
+
+ Node node = base;
+ String name = path.path;
+
+ if (sorter != null) {
+ deSorter = sorter;
+ }
+
+ if (deSorter != null) {
+ Collections.sort(members, deSorter);
+ deSorter = null; // sort only once
+ }
+
+ if (name != null && name.length() > 0) {
+ Element element = document.createElement(name);
+ node = node.appendChild(element);
+ }
+
+ for (XAnnotatedMember annotatedMember : members) {
+ annotatedMember.decode(instance, node, document, filters);
+ }
+ }
+}
+
+class Sorter implements Comparator {
+
+ private final Map order = new HashMap();
+
+ Sorter(String[] order) {
+ for (int i = 0; i < order.length; i++) {
+ this.order.put(order[i], i);
+ }
+ }
+
+ public int compare(XAnnotatedMember o1, XAnnotatedMember o2) {
+ Integer order1 = order.get(o1.path.path);
+ Integer order2 = order.get(o2.path.path);
+ int n1 = order1 == null ? Integer.MAX_VALUE : order1;
+ int n2 = order2 == null ? Integer.MAX_VALUE : order2;
+ return n1 - n2;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedParent.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedParent.java
new file mode 100755
index 000000000..a951df185
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XAnnotatedParent.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.List;
+
+/**
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XAnnotatedParent extends XAnnotatedMember {
+
+ protected XAnnotatedParent(XMap xmap, XSetter setter, XGetter getter) {
+ super(xmap, setter, getter);
+ }
+
+ @Override
+ protected Object getValue(Context ctx, Element base) throws Exception {
+ return ctx.getParent();
+ }
+
+ @Override
+ public void decode(Object instance, Node base, Document document, List filters)
+ throws Exception {
+ // do nothing
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XFieldGetter.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XFieldGetter.java
new file mode 100755
index 000000000..35dee7267
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XFieldGetter.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import java.lang.reflect.Field;
+
+/**
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+
+public class XFieldGetter implements XGetter {
+
+ /**
+ * field
+ */
+ private Field field;
+
+ public XFieldGetter(Field field) {
+ this.field = field;
+ this.field.setAccessible(true);
+ }
+
+ /**
+ * @see XGetter#getType()
+ */
+ public Class> getType() {
+ return field.getType();
+ }
+
+ /**
+ * @see XGetter#getValue(Object)
+ */
+ public Object getValue(Object instance) throws Exception {
+ if (instance == null) {
+ return null;
+ }
+ return field.get(instance);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XFieldSetter.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XFieldSetter.java
new file mode 100755
index 000000000..04b3ffe95
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XFieldSetter.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import java.lang.reflect.Field;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XFieldSetter implements XSetter {
+
+ /**
+ * field
+ */
+ private final Field field;
+
+ public XFieldSetter(Field field) {
+ this.field = field;
+ this.field.setAccessible(true);
+ }
+
+ /**
+ * @see XSetter#getType()
+ */
+ public Class> getType() {
+ return field.getType();
+ }
+
+ /**
+ * @see XSetter#setValue(Object, Object)
+ */
+ public void setValue(Object instance, Object value) throws IllegalAccessException {
+ field.set(instance, value);
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XGetter.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XGetter.java
new file mode 100644
index 000000000..0af5aa17b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XGetter.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public interface XGetter {
+
+ /**
+ * Get type
+ *
+ * @return Class
+ */
+ Class> getType();
+
+ /**
+ * Get value
+ *
+ * @throws Exception any exception
+ */
+ Object getValue(Object instance) throws Exception;
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMap.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMap.java
new file mode 100644
index 000000000..6dadefa78
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMap.java
@@ -0,0 +1,624 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import com.alipay.sofa.common.xmap.annotation.XContent;
+import com.alipay.sofa.common.xmap.annotation.XMemberAnnotation;
+import com.alipay.sofa.common.xmap.annotation.XNode;
+import com.alipay.sofa.common.xmap.annotation.XNodeList;
+import com.alipay.sofa.common.xmap.annotation.XNodeMap;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import com.alipay.sofa.common.xmap.annotation.XParent;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.beans.Introspector;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * XMap maps an XML file to a java object.
+ * The mapping is described by annotations on java objects.
+ * The following annotations are supported:
+ *
+ * {@link XObject}
+ * Mark the object as being mappable to an XML node
+ * {@link XNode}
+ * Map an XML node to a field of a mappable object
+ * {@link XNodeList}
+ * Map an list of XML nodes to a field of a mappable object
+ * {@link XNodeMap}
+ * Map an map of XML nodes to a field of a mappable object
+ * {@link XContent}
+ * Map an XML node content to a field of a mappable object
+ * {@link XParent}
+ * Map a field of the current mappable object to the parent object if any exists
+ * The parent object is the mappable object containing the current object as a field
+ *
+ * The mapping is done in 2 steps:
+ *
+ * The XML file is loaded as a DOM document
+ * The DOM document is parsed and the nodes mapping is resolved
+ *
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ */
+
+public class XMap {
+
+ // top level objects
+ protected final Map roots;
+
+ // the scanned objects
+ protected final Map objects;
+
+ protected final Map factories;
+
+ private static final String DDD = "http://apache.org/xml/features/disallow-doctype-decl";
+
+ /**
+ * Creates a new XMap object.
+ */
+ public XMap() {
+ objects = new Hashtable<>();
+ roots = new Hashtable<>();
+ factories = new Hashtable<>(XValueFactory.defaultFactories);
+ }
+
+ /**
+ * Gets the value factory used for objects of the given class.
+ * Value factories are used to decode values from XML strings.
+ *
+ * @param type the object type
+ * @return the value factory if any, null otherwise
+ */
+ public XValueFactory getValueFactory(Class type) {
+ return factories.get(type);
+ }
+
+ /**
+ * Sets a custom value factory for the given class.
+ * Value factories are used to decode values from XML strings.
+ *
+ * @param type the object type
+ * @param factory the value factory to use for the given type
+ */
+ public void setValueFactory(Class type, XValueFactory factory) {
+ factories.put(type, factory);
+ }
+
+ /**
+ * Gets a list of scanned objects.
+ * Scanned objects are annotated objects that was registered
+ * by this XMap instance.
+ *
+ * @return list of scanned objects.
+ */
+ public Collection getScannedObjects() {
+ return objects.values();
+ }
+
+ /**
+ * Gets the root objects.
+ * Root objects are scanned objects that can be mapped to XML elements
+ * that are not part from other objects.
+ *
+ * @return the root objects
+ */
+ public Collection getRootObjects() {
+ return roots.values();
+ }
+
+ /**
+ * Registers a mappable object class.
+ * The class will be scanned for XMap annotations
+ * and a mapping description is created.
+ *
+ * @param klass the object class
+ * @return the mapping description
+ */
+ @SuppressWarnings("unchecked")
+ public XAnnotatedObject register(Class klass) {
+ XAnnotatedObject xao = objects.get(klass);
+ if (xao == null) { // avoid scanning twice
+ XObject xob = checkObjectAnnotation(klass, klass.getClassLoader());
+ if (xob != null) {
+ xao = new XAnnotatedObject(this, klass, xob);
+ objects.put(xao.klass, xao);
+ scan(xao);
+ String key = xob.value();
+ if (key.length() > 0) {
+ roots.put(xao.path.path, xao);
+ }
+ }
+ }
+ return xao;
+ }
+
+ /**
+ * Registers a mappable object class.
+ * The class will be scanned for XMap annotations
+ * and a mapping description is created.
+ *
+ * @param klass the object class
+ * @return the mapping description
+ */
+ @SuppressWarnings("unchecked")
+ public XAnnotatedObject register(Class klass, boolean isParent) {
+ XAnnotatedObject xao = objects.get(klass);
+ if (xao == null) { // avoid scanning twice
+ XObject xob = checkObjectAnnotation(klass, klass.getClassLoader());
+ if (xob != null) {
+ xao = new XAnnotatedObject(this, klass, xob);
+ objects.put(xao.klass, xao);
+ if (isParent) {
+ scanParent(xao);
+ } else {
+ scan(xao);
+ }
+
+ String key = xob.value();
+ if (key.length() > 0) {
+ roots.put(xao.path.path, xao);
+ }
+ }
+ }
+ return xao;
+ }
+
+ private void scan(XAnnotatedObject xob) {
+ Field[] fields = xob.klass.getDeclaredFields();
+ for (Field field : fields) {
+ Annotation anno = checkMemberAnnotation(field);
+ if (anno != null) {
+ XAnnotatedMember member = createFieldMember(field, anno);
+ xob.addMember(member);
+ }
+ }
+
+ Method[] methods = xob.klass.getDeclaredMethods();
+ for (Method method : methods) {
+ // we accept only methods with one parameter
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) {
+ continue;
+ }
+ Annotation anno = checkMemberAnnotation(method);
+ if (anno != null) {
+ XAnnotatedMember member = createMethodMember(method, xob.klass, anno);
+ xob.addMember(member);
+ }
+ }
+ }
+
+ private void scanParent(XAnnotatedObject xob) {
+ Class scanClass = xob.klass;
+
+ for (; scanClass != Object.class; scanClass = scanClass.getSuperclass()) {
+ for (Method method : scanClass.getDeclaredMethods()) {
+ // we accept only methods with one parameter
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) {
+ continue;
+ }
+ Annotation anno = checkMemberAnnotation(method);
+ if (anno != null) {
+ XAnnotatedMember member = createMethodMember(method, xob.klass, anno);
+ xob.addMember(member);
+ }
+ }
+
+ for (Field field : scanClass.getDeclaredFields()) {
+ Annotation anno = checkMemberAnnotation(field);
+ if (anno != null) {
+ XAnnotatedMember member = createFieldMember(field, anno);
+ xob.addMember(member);
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes the XML file at the given URL using a default context.
+ * Returns the first registered top level object that is found in the file.
+ * If not objects are found null is returned.
+ *
+ * @param url the XML file url
+ * @return the first register top level object
+ * @throws Exception
+ */
+ public Object load(URL url) throws Exception {
+ return load(new Context(), url.openStream());
+ }
+
+ /**
+ * Processes the XML file at the given URL and using the given contexts.
+ * Returns the first registered top level object that is found in the file.
+ *
+ * @param ctx the context to use
+ * @param url the XML file url
+ * @return the first register top level object
+ * @throws Exception any exception
+ */
+ public Object load(Context ctx, URL url) throws Exception {
+ return load(ctx, url.openStream());
+ }
+
+ /**
+ * Processes the XML content from the given input stream using a default context.
+ * Returns the first registered top level object that is found in the file.
+ *
+ * @param in the XML input source
+ * @return the first register top level object
+ * @throws Exception
+ */
+ public Object load(InputStream in) throws Exception {
+ return load(new Context(), in);
+ }
+
+ /**
+ * Processes the XML content from the given input stream using the given context.
+ * Return the first registered top level object that is found in the file.
+ *
+ * @param ctx the context to use
+ * @param in the input stream
+ * @return the first register top level object
+ * @throws Exception any exception
+ */
+ public Object load(Context ctx, InputStream in) throws Exception {
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature(DDD, true);
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(in);
+ return load(ctx, document.getDocumentElement());
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes the XML file at the given URL using a default context.
+ * Returns a list with all registered top level objects that are found in the file.
+ * If not objects are found, an empty list is returned.
+ *
+ * @param url the XML file url
+ * @return a list with all registered top level objects that are found in the file
+ * @throws Exception
+ */
+ public Object[] loadAll(URL url) throws Exception {
+ return loadAll(new Context(), url.openStream());
+ }
+
+ /**
+ * Processes the XML file at the given URL using the given context
+ * Return a list with all registered top level objects that are found in the file.
+ * If not objects are found an empty list is returned.
+ *
+ * @param ctx the context to use
+ * @param url the XML file url
+ * @return a list with all registered top level objects that are found in the file
+ * @throws Exception any exception
+ */
+ public Object[] loadAll(Context ctx, URL url) throws Exception {
+ return loadAll(ctx, url.openStream());
+ }
+
+ /**
+ * Processes the XML from the given input stream using the given context.
+ * Returns a list with all registered top level objects that are found in the file.
+ * If not objects are found, an empty list is returned.
+ *
+ * @param in the XML input stream
+ * @return a list with all registered top level objects that are found in the file
+ * @throws Exception any exception
+ */
+ public Object[] loadAll(InputStream in) throws Exception {
+ return loadAll(new Context(), in);
+ }
+
+ /**
+ * Processes the XML from the given input stream using the given context.
+ * Returns a list with all registered top level objects that are found in the file.
+ * If not objects are found, an empty list is returned.
+ *
+ * @param ctx the context to use
+ * @param in the XML input stream
+ * @return a list with all registered top level objects that are found in the file
+ * @throws Exception
+ */
+ public Object[] loadAll(Context ctx, InputStream in) throws Exception {
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature(DDD, true);
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(in);
+ return loadAll(ctx, document.getDocumentElement());
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes the given DOM element and return the first mappable object
+ * found in the element.
+ * A default context is used.
+ *
+ * @param root the element to process
+ * @return the first object found in this element or null if none
+ * @throws Exception
+ */
+ public Object load(Element root) throws Exception {
+ return load(new Context(), root);
+ }
+
+ /**
+ * Processes the given DOM element and return the first mappable object
+ * found in the element.
+ * The given context is used.
+ *
+ * @param ctx the context to use
+ * @param root the element to process
+ * @return the first object found in this element or null if none
+ * @throws Exception
+ */
+ public Object load(Context ctx, Element root) throws Exception {
+ // check if the current element is bound to an annotated object
+ String name = root.getNodeName();
+ XAnnotatedObject xob = roots.get(name);
+ if (xob != null) {
+ return xob.newInstance(new Context(), root);
+ } else {
+ Node p = root.getFirstChild();
+ while (p != null) {
+ if (p.getNodeType() == Node.ELEMENT_NODE) {
+ // Recurse in the first child Element
+ return load((Element) p);
+ }
+ p = p.getNextSibling();
+ }
+ // We didn't find any Element
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public T load(Map map, String keyPrefix, Class klass) throws Exception {
+ XAnnotatedObject xob = objects.get(klass);
+ if (xob == null) {
+ xob = this.register(klass);
+ }
+
+ return (T) xob.newInstance(new Context(), map, keyPrefix);
+ }
+
+ /**
+ * Processes the given DOM element and return a list with all top-level
+ * mappable objects found in the element.
+ * The given context is used.
+ *
+ * @param ctx the context to use
+ * @param root the element to process
+ * @return the list of all top level objects found
+ * @throws Exception
+ */
+ public Object[] loadAll(Context ctx, Element root) throws Exception {
+ List result = new ArrayList();
+ loadAll(ctx, root, result);
+ return result.toArray();
+ }
+
+ /**
+ * Processes the given DOM element and return a list with all top-level
+ * mappable objects found in the element.
+ * The default context is used.
+ *
+ * @param root the element to process
+ * @return the list of all top level objects found
+ * @throws Exception
+ */
+ public Object[] loadAll(Element root) throws Exception {
+ return loadAll(new Context(), root);
+ }
+
+ /**
+ * Same as {@link com.alipay.sofa.common.xmap.XMap#loadAll(Element)} but put collected objects in the
+ * given collection.
+ *
+ * @param root the element to process
+ * @param result the collection where to collect objects
+ * @throws Exception
+ */
+ public void loadAll(Element root, Collection result) throws Exception {
+ loadAll(new Context(), root, result);
+ }
+
+ public Document asXml(Object contribution, List filters) throws Exception {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature(DDD, true);
+ factory.setNamespaceAware(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.newDocument();
+
+ if (contribution == null) {
+ return document;
+ }
+
+ XAnnotatedObject xao = objects.get(contribution.getClass());
+
+ if (xao != null) {
+ xao.decode(contribution, document, document, filters);
+ } // else TODO
+
+ return document;
+ }
+
+ public String asXmlString(Object contribution, String encoding, List filters)
+ throws Exception {
+ return asXmlString(contribution, encoding, filters, false);
+
+ }
+
+ public String asXmlString(Object contribution, String encoding, List filters,
+ boolean pretty) throws Exception {
+ Document doc = asXml(contribution, filters);
+
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
+ DOMSource source = new DOMSource(doc);
+ if (encoding != null && encoding.length() > 0) {
+ transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
+ }
+ if (pretty) {
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ } else {
+ transformer.setOutputProperty(OutputKeys.INDENT, "no");
+ }
+
+ StringWriter sw = new StringWriter();
+
+ StreamResult result = new StreamResult(sw);
+ transformer.transform(source, result);
+
+ return sw.toString();
+
+ }
+
+ /**
+ * Same as {@link com.alipay.sofa.common.xmap.XMap#loadAll(Context, Element)} but put collected objects in the
+ * given collection.
+ *
+ * @param ctx the context to use
+ * @param root the element to process
+ * @param result the collection where to collect objects
+ * @throws Exception
+ */
+ public void loadAll(Context ctx, Element root, Collection result) throws Exception {
+ // check if the current element is bound to an annotated object
+ String name = root.getNodeName();
+ XAnnotatedObject xob = roots.get(name);
+ if (xob != null) {
+ Object ob = xob.newInstance(ctx, root);
+ result.add(ob);
+ } else {
+ Node p = root.getFirstChild();
+ while (p != null) {
+ if (p.getNodeType() == Node.ELEMENT_NODE) {
+ loadAll(ctx, (Element) p, result);
+ }
+ p = p.getNextSibling();
+ }
+ }
+ }
+
+ protected static Annotation checkMemberAnnotation(AnnotatedElement ae) {
+ Annotation[] annos = ae.getAnnotations();
+ for (Annotation anno : annos) {
+ if (anno.annotationType().isAnnotationPresent(XMemberAnnotation.class)) {
+ return anno;
+ }
+ }
+ return null;
+ }
+
+ protected static XObject checkObjectAnnotation(AnnotatedElement ae, ClassLoader classLoader) {
+ return ae.getAnnotation(XObject.class);
+ }
+
+ private XAnnotatedMember createMember(Annotation annotation, XSetter setter, XGetter getter) {
+ XAnnotatedMember member = null;
+ int type = annotation.annotationType().getAnnotation(XMemberAnnotation.class).value();
+ if (type == XMemberAnnotation.NODE) {
+ member = new XAnnotatedMember(this, setter, getter, (XNode) annotation);
+ } else if (type == XMemberAnnotation.NODE_LIST) {
+ member = new XAnnotatedList(this, setter, getter, (XNodeList) annotation);
+ } else if (type == XMemberAnnotation.NODE_MAP) {
+ member = new XAnnotatedMap(this, setter, getter, (XNodeMap) annotation);
+ } else if (type == XMemberAnnotation.PARENT) {
+ member = new XAnnotatedParent(this, setter, getter);
+ } else if (type == XMemberAnnotation.CONTENT) {
+ member = new XAnnotatedContent(this, setter, getter, (XContent) annotation);
+ }
+ return member;
+ }
+
+ public final XAnnotatedMember createFieldMember(Field field, Annotation annotation) {
+ XSetter setter = new XFieldSetter(field);
+ XGetter getter = new XFieldGetter(field);
+ return createMember(annotation, setter, getter);
+ }
+
+ public final XAnnotatedMember createMethodMember(Method method, Class> clazz,
+ Annotation annotation) {
+ XSetter setter = new XMethodSetter(method);
+
+ String methodName = method.getName();
+ String name = Introspector.decapitalize(methodName.substring(3));
+ XGetter getter = new XMethodGetter(getGetterMethod(clazz, name), clazz, name);
+ return createMember(annotation, setter, getter);
+ }
+
+ public Method getGetterMethod(Class> clazz, String name) {
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ String methodName = method.getName();
+ if (methodName.matches("^(get|is).*") && method.getParameterTypes().length == 0) {
+ if (Introspector.decapitalize(methodName.substring(3)).equals(name)) {
+ return method;
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMethodGetter.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMethodGetter.java
new file mode 100755
index 000000000..520cc14d7
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMethodGetter.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import java.lang.reflect.Method;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XMethodGetter implements XGetter {
+
+ /**
+ * method
+ */
+ private Method method;
+
+ /**
+ * class
+ */
+ private Class> clazz;
+
+ /**
+ * bean name
+ */
+ private String name;
+
+ public XMethodGetter(Method method, Class> clazz, String name) {
+ this.method = method;
+ this.clazz = clazz;
+ this.name = name;
+ }
+
+ /**
+ * @see XGetter#getType()
+ */
+ public Class> getType() {
+ if (method == null) {
+ throw new IllegalArgumentException("no such getter method: " + clazz.getName() + '.'
+ + name);
+ }
+
+ return method.getReturnType();
+ }
+
+ /**
+ * @see XGetter#getValue(Object)
+ */
+ public Object getValue(Object instance) throws Exception {
+ if (method == null) {
+ throw new IllegalArgumentException("no such getter method: " + clazz.getName() + '.'
+ + name);
+ }
+
+ if (instance == null) {
+ return null;
+ }
+ return method.invoke(instance, new Object[0]);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMethodSetter.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMethodSetter.java
new file mode 100755
index 000000000..9f7c4d0ad
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XMethodSetter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XMethodSetter implements XSetter {
+
+ /**
+ * method
+ */
+ private final Method method;
+
+ public XMethodSetter(Method method) {
+ this.method = method;
+ this.method.setAccessible(true);
+ }
+
+ /**
+ * @see XSetter#getType()
+ */
+ public Class> getType() {
+ return method.getParameterTypes()[0];
+ }
+
+ /**
+ * @see XSetter#setValue(Object, Object)
+ */
+ public void setValue(Object instance, Object value) throws IllegalAccessException,
+ InvocationTargetException {
+ method.invoke(instance, value);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XSetter.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XSetter.java
new file mode 100644
index 000000000..ea8a31e91
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XSetter.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+/**
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public interface XSetter {
+
+ /**
+ * Gets the type of the object to be set by this setter.
+ *
+ * @return the setter object type
+ */
+ Class getType();
+
+ /**
+ * Sets the value of the underlying member.
+ *
+ * @param instance the instance of the object that owns this field
+ * @param value the value to set
+ * @throws Exception
+ */
+ void setValue(Object instance, Object value) throws Exception;
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XValueFactory.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XValueFactory.java
new file mode 100644
index 000000000..4990428d6
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/XValueFactory.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap;
+
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import org.w3c.dom.Node;
+
+import java.io.File;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * Value factories are used to decode values from XML strings.
+ * To register a new factory for a given XMap instance use the method
+ * {@link com.alipay.sofa.common.xmap.XMap#setValueFactory(Class, com.alipay.sofa.common.xmap.XValueFactory)}
+ *
+ * @author Bogdan Stefanescu
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public abstract class XValueFactory {
+
+ static final Map defaultFactories = new Hashtable();
+
+ public abstract Object getValue(Context ctx, String value);
+
+ public final Object getElementValue(Context ctx, Node element, boolean trim) {
+ String text = element.getTextContent();
+ return getValue(ctx, trim ? text.trim() : text);
+ }
+
+ public final Object getAttributeValue(Context ctx, Node element, String name) {
+ Node at = element.getAttributes().getNamedItem(name);
+ return at != null ? getValue(ctx, at.getNodeValue()) : null;
+ }
+
+ public static void addFactory(Class klass, XValueFactory factory) {
+ defaultFactories.put(klass, factory);
+ }
+
+ public static XValueFactory getFactory(Class type) {
+ return defaultFactories.get(type);
+ }
+
+ public static Object getValue(Context ctx, Class klass, String value) {
+ XValueFactory factory = defaultFactories.get(klass);
+ if (factory == null) {
+ return null;
+ }
+ return factory.getValue(ctx, value);
+ }
+
+ public static final XValueFactory STRING = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return value;
+ }
+ };
+
+ public static final XValueFactory INTEGER = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return Integer.valueOf(value);
+ }
+ };
+
+ public static final XValueFactory LONG = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return Long.valueOf(value);
+ }
+ };
+
+ public static final XValueFactory DOUBLE = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return Double.valueOf(value);
+ }
+ };
+
+ public static final XValueFactory FLOAT = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return Float.valueOf(value);
+ }
+ };
+
+ public static final XValueFactory BOOLEAN = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return Boolean.valueOf(value);
+ }
+ };
+
+ public static final XValueFactory DATE = new XValueFactory() {
+ final DateFormat df = new SimpleDateFormat(
+ "dd-MM-yyyy");
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ try {
+ return df.parse(value);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ };
+
+ public static final XValueFactory FILE = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return new File(value);
+ }
+ };
+
+ public static final XValueFactory URL = new XValueFactory() {
+ @Override
+ public Object getValue(Context ctx, String value) {
+ try {
+ return new java.net.URL(value);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ };
+
+ public static final XValueFactory CLASS = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ try {
+ return ctx.loadClass(value);
+ } catch (Throwable t) {
+ SofaLogger.error("load class error", t);
+ return null;
+ }
+ }
+ };
+
+ public static final XValueFactory RESOURCE = new XValueFactory() {
+
+ @Override
+ public Object getValue(Context ctx, String value) {
+ try {
+ return new Resource(
+ ctx.getResource(value));
+ } catch (Throwable t) {
+ SofaLogger
+ .error("new resource error", t);
+ return null;
+ }
+ }
+ };
+
+ public static final XValueFactory SHORT = new XValueFactory() {
+ @Override
+ public Object getValue(Context ctx, String value) {
+ return Short.valueOf(value);
+ }
+ };
+
+ public static final XValueFactory CHAR = new XValueFactory() {
+ @Override
+ public Object getValue(Context ctx, String value) {
+ try {
+ return value.toCharArray()[0];
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+ };
+
+ public static final XValueFactory BYTE = new XValueFactory() {
+ @Override
+ public Object getValue(Context ctx, String value) {
+ try {
+ return value.getBytes()[0];
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+ };
+
+ static {
+ addFactory(String.class, STRING);
+ addFactory(Integer.class, INTEGER);
+ addFactory(Long.class, LONG);
+ addFactory(Float.class, FLOAT);
+ addFactory(Double.class, DOUBLE);
+ addFactory(Date.class, DATE);
+ addFactory(Boolean.class, BOOLEAN);
+ addFactory(File.class, FILE);
+ addFactory(java.net.URL.class, URL);
+ addFactory(Short.class, SHORT);
+ addFactory(Character.class, CHAR);
+ addFactory(Byte.class, BYTE);
+
+ addFactory(int.class, INTEGER);
+ addFactory(long.class, LONG);
+ addFactory(double.class, DOUBLE);
+ addFactory(float.class, FLOAT);
+ addFactory(boolean.class, BOOLEAN);
+ addFactory(short.class, SHORT);
+ addFactory(char.class, CHAR);
+ addFactory(byte.class, BYTE);
+
+ addFactory(Class.class, CLASS);
+ addFactory(Resource.class, RESOURCE);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XContent.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XContent.java
new file mode 100755
index 000000000..6460fd3d4
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XContent.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that injects the content of the current node as an XML string or
+ * DocumentFragment depending on the field type.
+ *
+ * @author Bogdan Stefanescu
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.CONTENT)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XContent {
+
+ /**
+ * An xpathy expression specifying the XML node to bind to.
+ *
+ * @return the node xpath
+ */
+ String value() default "";
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XMemberAnnotation.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XMemberAnnotation.java
new file mode 100644
index 000000000..83dd7f1b8
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XMemberAnnotation.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation to identify XMap annotations.
+ *
+ * This annotation has a single parameter "value" of type int
that specifies the type of the annotation.
+ *
+ * @author Bogdan Stefanescu
+ * @since 2.6.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface XMemberAnnotation {
+
+ int NODE = 1;
+ int NODE_LIST = 2;
+ int NODE_MAP = 3;
+ int PARENT = 4;
+ int CONTENT = 5;
+ int NODE_SPRING = 6;
+ int NODE_LIST_SPRING = 7;
+ int NODE_MAP_SPRING = 8;
+
+ /**
+ * The type of the annotation.
+ *
+ * @return the type of the annotation.
+ */
+ int value();
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNode.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNode.java
new file mode 100644
index 000000000..6b3f5216a
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNode.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Bogdan Stefanescu
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.NODE)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XNode {
+
+ /**
+ * An xpathy expression specifying the XML node to bind to.
+ */
+ String value() default "";
+
+ /**
+ * Whether to trim text content for element nodes.
+ *
+ * Ignored for attribute nodes.
+ *
+ */
+ boolean trim() default true;
+
+ /**
+ * is or not cdata
+ */
+ boolean cdata() default false;
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNodeList.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNodeList.java
new file mode 100644
index 000000000..889728137
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNodeList.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Bogdan Stefanescu
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.NODE_LIST)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XNodeList {
+
+ /**
+ * A path expression specifying the XML node to bind to.
+ *
+ * @return the node xpath
+ */
+ String value();
+
+ /**
+ * Whether to trim text content for element nodes.
+ *
+ * @return
+ */
+ boolean trim() default true;
+
+ /**
+ * The type of a collection object.
+ *
+ * @return the type of items
+ */
+ Class type();
+
+ /**
+ * The type of the objects in this collection.
+ *
+ * @return the type of items
+ */
+ Class componentType();
+
+ /**
+ * is or not cdata
+ */
+ boolean cdata() default false;
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNodeMap.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNodeMap.java
new file mode 100644
index 000000000..cc5270a31
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XNodeMap.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Bogdan Stefanescu
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.NODE_MAP)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XNodeMap {
+
+ /**
+ * A path expression specifying the XML node to bind to.
+ *
+ * @return the node xpath
+ */
+ String value();
+
+ /**
+ * Whether to trim text content for element nodes.
+ *
+ * @return
+ */
+ boolean trim() default true;
+
+ /**
+ * The path relative to the current node
+ * (which is located by {@link com.alipay.sofa.common.xmap.annotation.XNodeMap#value()}) which contain
+ * the map key to be used.
+ *
+ * @return
+ */
+ String key();
+
+ /**
+ * The type of collection object.
+ *
+ * @return the type of items
+ */
+ Class type();
+
+ /**
+ * The type of the objects in this collection.
+ *
+ * @return the type of items
+ */
+ Class componentType();
+
+ /**
+ * is or not cdata
+ */
+ boolean cdata() default false;
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XObject.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XObject.java
new file mode 100644
index 000000000..731c0cacf
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XObject.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Bogdan Stefanescu
+ * @since 2.6.0
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XObject {
+
+ /**
+ * An xpath expression specifying the XML node to bind to.
+ *
+ * @return the node xpath
+ */
+ String value() default "";
+
+ /**
+ *
+ * @return
+ */
+ String[] order() default {};
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XParent.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XParent.java
new file mode 100755
index 000000000..e4ce29c00
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/XParent.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Bogdan Stefanescu
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.PARENT)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XParent {
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedListSpring.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedListSpring.java
new file mode 100755
index 000000000..9b5fc1d4d
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedListSpring.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation.spring;
+
+import com.alipay.sofa.common.xmap.Context;
+import com.alipay.sofa.common.xmap.DOMHelper;
+import com.alipay.sofa.common.xmap.Path;
+import com.alipay.sofa.common.xmap.XAnnotatedList;
+import com.alipay.sofa.common.xmap.XAnnotatedMember;
+import com.alipay.sofa.common.xmap.XGetter;
+import com.alipay.sofa.common.xmap.XMap;
+import com.alipay.sofa.common.xmap.XSetter;
+import com.alipay.sofa.common.xmap.spring.XNodeListSpring;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class XAnnotatedListSpring extends XAnnotatedList {
+
+ /**
+ * dom visitor
+ */
+ protected static final ElementValueVisitor elementVisitor = new ElementValueVisitor();
+ protected static final AttributeValueVisitor attributeVisitor = new AttributeValueVisitor();
+
+ private XAnnotatedSpringObject xaso;
+
+ protected XAnnotatedListSpring(XMap xmap, XSetter setter, XGetter getter) {
+ super(xmap, setter, getter);
+ }
+
+ public XAnnotatedListSpring(XMap xmap, XSetter setter, XGetter getter, XNodeListSpring anno,
+ XAnnotatedSpringObject xaso) {
+ super(xmap, setter, getter);
+ path = new Path(anno.value());
+ trim = anno.trim();
+ type = anno.type();
+ componentType = anno.componentType();
+ this.xaso = xaso;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Object getValue(Context ctx, Element base) throws Exception {
+ ArrayList values = new ArrayList();
+ if (path.attribute != null) {
+ // attribute list
+ DOMHelper.visitNodes(ctx, this, base, path, attributeVisitor, values);
+ } else {
+ // element list
+ DOMHelper.visitNodes(ctx, this, base, path, elementVisitor, values);
+ }
+
+ if (type != ArrayList.class) {
+ if (type.isArray() && !componentType.isPrimitive()) {
+ values.toArray((Object[]) Array.newInstance(componentType, values.size()));
+ } else {
+ Collection col = (Collection) type.newInstance();
+ col.addAll(values);
+ return col;
+ }
+ }
+ return values;
+ }
+
+ public void setXaso(XAnnotatedSpringObject xaso) {
+ this.xaso = xaso;
+ }
+
+ public XAnnotatedSpringObject getXaso() {
+ return xaso;
+ }
+}
+
+class ElementValueVisitor extends DOMHelper.NodeVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection result) {
+ String val = node.getTextContent();
+ if (val != null && val.length() > 0) {
+ if (xam.trim)
+ val = val.trim();
+ Object object = XMapSpringUtil.getSpringObject(
+ ((XAnnotatedListSpring) xam).componentType, val, ((XAnnotatedListSpring) xam)
+ .getXaso().getApplicationContext());
+ if (object != null)
+ result.add(object);
+ }
+ }
+}
+
+class AttributeValueVisitor extends DOMHelper.NodeVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, Collection result) {
+ String val = node.getNodeValue();
+ if (val != null && val.length() > 0) {
+ if (xam.trim)
+ val = val.trim();
+ Object object = XMapSpringUtil.getSpringObject(
+ ((XAnnotatedListSpring) xam).componentType, val, ((XAnnotatedListSpring) xam)
+ .getXaso().getApplicationContext());
+ if (object != null)
+ result.add(object);
+ }
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedMapSpring.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedMapSpring.java
new file mode 100755
index 000000000..93b2245db
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedMapSpring.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation.spring;
+
+import com.alipay.sofa.common.xmap.Context;
+import com.alipay.sofa.common.xmap.DOMHelper;
+import com.alipay.sofa.common.xmap.Path;
+import com.alipay.sofa.common.xmap.XAnnotatedMap;
+import com.alipay.sofa.common.xmap.XAnnotatedMember;
+import com.alipay.sofa.common.xmap.XGetter;
+import com.alipay.sofa.common.xmap.XMap;
+import com.alipay.sofa.common.xmap.XSetter;
+import com.alipay.sofa.common.xmap.spring.XNodeMapSpring;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.Map;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class XAnnotatedMapSpring extends XAnnotatedMap {
+
+ /**
+ * dom visitor
+ */
+ protected static final ElementValueMapVisitor elementVisitor = new ElementValueMapVisitor();
+ protected static final AttributeValueMapVisitor attributeVisitor = new AttributeValueMapVisitor();
+
+ private XAnnotatedSpringObject xaso;
+
+ public XAnnotatedMapSpring(XMap xmap, XSetter setter, XGetter getter, XNodeMapSpring anno,
+ XAnnotatedSpringObject xaso) {
+ super(xmap, setter, getter, null);
+ this.setXaso(xaso);
+ path = new Path(anno.value());
+ trim = anno.trim();
+ key = new Path(anno.key());
+ type = anno.type();
+ componentType = anno.componentType();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Object getValue(Context ctx, Element base) throws IllegalAccessException,
+ InstantiationException {
+ Map values = (Map) type.newInstance();
+ if (path.attribute != null) {
+ // attribute list
+ DOMHelper.visitMapNodes(ctx, this, base, path, attributeVisitor, values);
+ } else {
+ // element list
+ DOMHelper.visitMapNodes(ctx, this, base, path, elementVisitor, values);
+ }
+ return values;
+ }
+
+ public void setXaso(XAnnotatedSpringObject xaso) {
+ this.xaso = xaso;
+ }
+
+ public XAnnotatedSpringObject getXaso() {
+ return xaso;
+ }
+}
+
+class ElementValueMapVisitor extends DOMHelper.NodeMapVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key,
+ Map result) {
+ String val = node.getTextContent();
+ if (val != null && val.length() > 0) {
+ if (xam.trim)
+ val = val.trim();
+ Object object = XMapSpringUtil.getSpringObject(
+ ((XAnnotatedMapSpring) xam).componentType, val, ((XAnnotatedMapSpring) xam)
+ .getXaso().getApplicationContext());
+ if (object != null)
+ result.put(key, object);
+ }
+ }
+}
+
+class AttributeValueMapVisitor extends DOMHelper.NodeMapVisitor {
+
+ @Override
+ public void visitNode(Context ctx, XAnnotatedMember xam, Node node, String key,
+ Map result) {
+ String val = node.getNodeValue();
+ if (val != null && val.length() > 0) {
+ if (xam.trim)
+ val = val.trim();
+ Object object = XMapSpringUtil.getSpringObject(
+ ((XAnnotatedMapSpring) xam).componentType, val, ((XAnnotatedMapSpring) xam)
+ .getXaso().getApplicationContext());
+ if (object != null)
+ result.put(key, object);
+ }
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedSpring.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedSpring.java
new file mode 100755
index 000000000..19df1f034
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedSpring.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation.spring;
+
+import com.alipay.sofa.common.xmap.Context;
+import com.alipay.sofa.common.xmap.Path;
+import com.alipay.sofa.common.xmap.XAnnotatedMember;
+import com.alipay.sofa.common.xmap.XGetter;
+import com.alipay.sofa.common.xmap.XMap;
+import com.alipay.sofa.common.xmap.XSetter;
+import com.alipay.sofa.common.xmap.spring.XNodeSpring;
+import org.w3c.dom.Element;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XAnnotatedSpring extends XAnnotatedMember {
+
+ public XAnnotatedSpringObject xaso;
+
+ protected XAnnotatedSpring(XMap xmap, XSetter setter, XGetter getter) {
+ super(xmap, setter, getter);
+ }
+
+ public XAnnotatedSpring(XMap xmap, XSetter setter, XGetter getter, XNodeSpring anno,
+ XAnnotatedSpringObject xaso) {
+ super(xmap, setter, getter);
+ path = new Path(anno.value());
+ trim = anno.trim();
+ type = setter.getType();
+ this.xaso = xaso;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Object getValue(Context ctx, Element base) throws Exception {
+ // scalar field
+ if (type == Element.class) {
+ // allow DOM elements as values
+ return base;
+ }
+ return XMapSpringUtil.getSpringOjbect(this, xaso.getApplicationContext(), base);
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedSpringObject.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedSpringObject.java
new file mode 100755
index 000000000..b260162f7
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XAnnotatedSpringObject.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation.spring;
+
+import com.alipay.sofa.common.xmap.XAnnotatedObject;
+import com.alipay.sofa.common.xmap.XMap;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ * */
+public class XAnnotatedSpringObject extends XAnnotatedObject {
+
+ private ApplicationContext applicationContext;
+
+ public XAnnotatedSpringObject(XMap xmap, Class klass, XObject xob,
+ ApplicationContext applicationContext) {
+ super(xmap, klass, xob);
+ this.applicationContext = applicationContext;
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public ApplicationContext getApplicationContext() {
+ return applicationContext;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XMapSpring.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XMapSpring.java
new file mode 100644
index 000000000..4ddfeaf2c
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XMapSpring.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation.spring;
+
+import com.alipay.sofa.common.xmap.XAnnotatedMember;
+import com.alipay.sofa.common.xmap.XAnnotatedObject;
+import com.alipay.sofa.common.xmap.XFieldGetter;
+import com.alipay.sofa.common.xmap.XFieldSetter;
+import com.alipay.sofa.common.xmap.XGetter;
+import com.alipay.sofa.common.xmap.XMap;
+import com.alipay.sofa.common.xmap.XMethodGetter;
+import com.alipay.sofa.common.xmap.XMethodSetter;
+import com.alipay.sofa.common.xmap.XSetter;
+import com.alipay.sofa.common.xmap.annotation.XMemberAnnotation;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import com.alipay.sofa.common.xmap.spring.XNodeListSpring;
+import com.alipay.sofa.common.xmap.spring.XNodeMapSpring;
+import com.alipay.sofa.common.xmap.spring.XNodeSpring;
+import org.springframework.context.ApplicationContext;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * Integrate XMap with spring
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XMapSpring extends XMap {
+
+ @SuppressWarnings("unchecked")
+ public XAnnotatedObject register(Class klass, ApplicationContext applicationContext) {
+ XAnnotatedObject xao = objects.get(klass);
+ if (xao == null) { // avoid scanning twice
+ XObject xob = checkObjectAnnotation(klass, klass.getClassLoader());
+ if (xob != null) {
+ xao = new XAnnotatedSpringObject(this, klass, xob, applicationContext);
+ objects.put(xao.klass, xao);
+ scan(xao);
+ String key = xob.value();
+ if (key.length() > 0) {
+ roots.put(xao.path.path, xao);
+ }
+ }
+ }
+ return xao;
+ }
+
+ /**
+ * Scan Field
+ *
+ * @param xob XAnnotated Object
+ */
+ private void scan(XAnnotatedObject xob) {
+ Field[] fields = xob.klass.getDeclaredFields();
+ for (Field field : fields) {
+ Annotation anno = checkMemberAnnotation(field);
+ if (anno != null) {
+ XAnnotatedMember member = createFieldMember(field, anno);
+ if (member == null) {
+ member = createExtendFieldMember(field, anno, xob);
+ }
+ xob.addMember(member);
+ }
+ }
+
+ Method[] methods = xob.klass.getDeclaredMethods();
+ for (Method method : methods) {
+ // we accept only methods with one parameter
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) {
+ continue;
+ }
+ Annotation anno = checkMemberAnnotation(method);
+ if (anno != null) {
+ XAnnotatedMember member = createMethodMember(method, xob.klass, anno);
+ if (member == null) {
+ member = createExtendMethodMember(method, anno, xob);
+ }
+ xob.addMember(member);
+ }
+ }
+ }
+
+ private XAnnotatedMember createExtendFieldMember(Field field, Annotation annotation,
+ XAnnotatedObject xob) {
+ XSetter setter = new XFieldSetter(field);
+ XGetter getter = new XFieldGetter(field);
+ return createExtendMember(annotation, setter, getter, xob);
+ }
+
+ public final XAnnotatedMember createExtendMethodMember(Method method, Annotation annotation,
+ XAnnotatedObject xob) {
+ XSetter setter = new XMethodSetter(method);
+ XGetter getter = new XMethodGetter(null, null, null);
+ return createExtendMember(annotation, setter, getter, xob);
+ }
+
+ private XAnnotatedMember createExtendMember(Annotation annotation, XSetter setter,
+ XGetter getter, XAnnotatedObject xob) {
+ XAnnotatedMember member = null;
+ int type = annotation.annotationType().getAnnotation(XMemberAnnotation.class).value();
+ if (type == XMemberAnnotation.NODE_SPRING) {
+ member = new XAnnotatedSpring(this, setter, getter, (XNodeSpring) annotation,
+ (XAnnotatedSpringObject) xob);
+ } else if (type == XMemberAnnotation.NODE_LIST_SPRING) {
+ member = new XAnnotatedListSpring(this, setter, getter, (XNodeListSpring) annotation,
+ (XAnnotatedSpringObject) xob);
+ } else if (type == XMemberAnnotation.NODE_MAP_SPRING) {
+ member = new XAnnotatedMapSpring(this, setter, getter, (XNodeMapSpring) annotation,
+ (XAnnotatedSpringObject) xob);
+ }
+ return member;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XMapSpringUtil.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XMapSpringUtil.java
new file mode 100755
index 000000000..17be7388a
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/annotation/spring/XMapSpringUtil.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.annotation.spring;
+
+import com.alipay.sofa.common.xmap.DOMHelper;
+import com.alipay.sofa.common.xmap.XAnnotatedMember;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
+import org.w3c.dom.Element;
+
+/**
+ * XMapSpring util
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ *
+ */
+public class XMapSpringUtil {
+
+ /**
+ * Get spring object
+ * @param type type
+ * @param beanName bean name
+ * @param applicationContext application context
+ * @return spring object
+ */
+ public static Object getSpringObject(Class type, String beanName,
+ ApplicationContext applicationContext) {
+ if (type == Resource.class) {
+ return applicationContext.getResource(beanName);
+ } else {
+ return applicationContext.getBean(beanName, type);
+ }
+ }
+
+ /**
+ * Get spring object
+ * @param xam XAnnotated member
+ * @param applicationContext application context
+ * @param base element base
+ * @return
+ */
+ public static Object getSpringOjbect(XAnnotatedMember xam,
+ ApplicationContext applicationContext, Element base) {
+ String val = DOMHelper.getNodeValue(base, xam.path);
+ if (val != null && val.length() > 0) {
+ if (xam.trim) {
+ val = val.trim();
+ }
+ return getSpringObject(xam.type, val, applicationContext);
+ }
+ return null;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeListSpring.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeListSpring.java
new file mode 100644
index 000000000..871170b55
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeListSpring.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.spring;
+
+import com.alipay.sofa.common.xmap.annotation.XMemberAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.NODE_LIST_SPRING)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XNodeListSpring {
+
+ /**
+ * Get the node xpath
+ *
+ * @return the node xpath
+ */
+ String value();
+
+ /**
+ * Get whether need to trim or not
+ *
+ * @return trim or not
+ */
+ boolean trim() default true;
+
+ /**
+ * Get the type of items
+ *
+ * @return the type of items
+ */
+ Class type();
+
+ /**
+ * Get the type of items
+ *
+ * @return the type of items
+ */
+ Class componentType();
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeMapSpring.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeMapSpring.java
new file mode 100755
index 000000000..ab60e9694
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeMapSpring.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.spring;
+
+import com.alipay.sofa.common.xmap.annotation.XMemberAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.NODE_MAP_SPRING)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XNodeMapSpring {
+
+ /**
+ * Get the node xpath
+ *
+ * @return the node xpath
+ */
+ String value();
+
+ /**
+ * Whether need to trim or not
+ *
+ * @return trim or not
+ */
+ boolean trim() default true;
+
+ /**
+ * Get the key path
+ *
+ * @return key path
+ */
+ String key();
+
+ /**
+ * Get the type of items
+ *
+ * @return the type of items
+ */
+ Class type();
+
+ /**
+ * Get the type of items
+ *
+ * @return the type of items
+ */
+ Class componentType();
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeSpring.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeSpring.java
new file mode 100755
index 000000000..dec644585
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/common/xmap/spring/XNodeSpring.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.common.xmap.spring;
+
+import com.alipay.sofa.common.xmap.annotation.XMemberAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+@XMemberAnnotation(XMemberAnnotation.NODE_SPRING)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XNodeSpring {
+
+ /**
+ * Get the node xpath
+ *
+ * @return the node xpath
+ */
+ String value();
+
+ /**
+ * Whether need to trim or not
+ *
+ * @return trim or not
+ */
+ boolean trim() default true;
+
+ /**
+ * Get the type of items
+ *
+ * @return the type of items
+ */
+ Class type() default Object.class;
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaParameter.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaParameter.java
new file mode 100644
index 000000000..ddc88c17f
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaParameter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.api.annotation;
+
+/**
+ * Configure parameters by annotation
+ *
+ * @author ScienJus
+ */
+public @interface SofaParameter {
+
+ /**
+ * Parameter key
+ * @return
+ */
+ String key();
+
+ /**
+ * Parameter value
+ * @return
+ */
+ String value();
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReferenceBinding.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReferenceBinding.java
index 635cf151b..3de1eda97 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReferenceBinding.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaReferenceBinding.java
@@ -101,13 +101,30 @@
String registry() default "";
/**
- * dalay init connection
+ * delay init connection
*
* @return
*/
boolean lazy() default false;
+ /**
+ * specify serialize type
+ *
+ * @return
+ */
String serializeType() default "";
+ /**
+ * specify load balance type
+ *
+ * @return
+ */
String loadBalancer() default "";
+
+ /**
+ * parameters of consumer
+ *
+ * @return parameters of consumer
+ */
+ SofaParameter[] parameters() default {};
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaServiceBinding.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaServiceBinding.java
index 1b23e52dd..80c8efbdf 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaServiceBinding.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/annotation/SofaServiceBinding.java
@@ -56,7 +56,7 @@
String[] filters() default {};
/**
- * custorm thread pool for current service
+ * custom thread pool for current service
*
* @return custom thread pool
*/
@@ -76,5 +76,17 @@
*/
int timeout() default 3000;
+ /**
+ * specify serialize type
+ *
+ * @return
+ */
String serializeType() default "";
+
+ /**
+ * parameters of service
+ *
+ * @return parameters of service
+ */
+ SofaParameter[] parameters() default {};
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/aware/ExtensionClientAware.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/aware/ExtensionClientAware.java
new file mode 100644
index 000000000..b33f0ee04
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/aware/ExtensionClientAware.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.api.aware;
+
+import com.alipay.sofa.runtime.api.client.ExtensionClient;
+
+/**
+ * Interface used to implemented by a Spring bean who want to get an instance of {@link ExtensionClient}. Sample usage:
+ *
+ *
+ *
+ * public class ExtensionClientBean implements ExtensionClientAware {
+ *
+ * private ExtensionClient extensionClient;
+ *
+ * @Override
+ * public void setExtensionClient(ExtensionClient extensionClient) {
+ * this.clientFactory = extensionClient;
+ * }
+ *
+ * public ExtensionClient getClientFactory() {
+ * return extensionClient;
+ * }
+ * }
+ *
+ *
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public interface ExtensionClientAware {
+
+ /**
+ * Set the instance of {@link ExtensionClient} to the Spring bean that implement this interface.
+ *
+ * @param extensionClient ExtensionClient The instance of {@link ExtensionClient}
+ */
+ void setExtensionClient(ExtensionClient extensionClient);
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/ExtensionClient.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/ExtensionClient.java
new file mode 100644
index 000000000..27d31775d
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/ExtensionClient.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.api.client;
+
+import com.alipay.sofa.runtime.api.client.param.ExtensionParam;
+import com.alipay.sofa.runtime.api.client.param.ExtensionPointParam;
+
+/**
+ * Programming API for Extension/Extension-Point
+ *
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public interface ExtensionClient {
+
+ /**
+ * publish extension
+ *
+ * @param extensionParam extension param
+ */
+ void publishExtension(ExtensionParam extensionParam);
+
+ /**
+ * publish extension-point
+ *
+ * @param extensionPointParam extension point param
+ */
+ void publishExtensionPoint(ExtensionPointParam extensionPointParam);
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/param/ExtensionParam.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/param/ExtensionParam.java
new file mode 100644
index 000000000..5c4117407
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/param/ExtensionParam.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.api.client.param;
+
+import org.w3c.dom.Element;
+
+/**
+ * Extension param
+ *
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionParam {
+ // target extension point name
+ private String targetName;
+ // target bean name of extension
+ private String targetInstanceName;
+ // XML Contribution element
+ private Element element;
+
+ /**
+ * Get the target extension point name
+ *
+ * @return The target extension point name
+ */
+ public String getTargetName() {
+ return targetName;
+ }
+
+ /**
+ * Set the target bean name of extension
+ *
+ * @param targetName The target bean name of extension
+ */
+ public void setTargetName(String targetName) {
+ this.targetName = targetName;
+ }
+
+ /**
+ * Get the XML Contribution element
+ *
+ * @return The XML Contribution element
+ */
+ public Element getElement() {
+ return element;
+ }
+
+ /**
+ * Set the XML Contribution element
+ *
+ * @param element The XML Contribution element
+ */
+ public void setElement(Element element) {
+ this.element = element;
+ }
+
+ /**
+ * Get the target bean name of extension
+ *
+ * @return The target target bean name of extension
+ */
+ public String getTargetInstanceName() {
+ return targetInstanceName;
+ }
+
+ /**
+ * Set the target bean name of extension
+ *
+ * @param targetInstanceName The the target bean name of extension
+ */
+ public void setTargetInstanceName(String targetInstanceName) {
+ this.targetInstanceName = targetInstanceName;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/param/ExtensionPointParam.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/param/ExtensionPointParam.java
new file mode 100644
index 000000000..86d23d60d
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/client/param/ExtensionPointParam.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.api.client.param;
+
+/**
+ * Extension-Point Param
+ *
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionPointParam {
+ // name of the extension point
+ private String name;
+ // target bean name of the extension point
+ private String targetName;
+ // target bean of the extension point
+ private Object target;
+ // contribution description class
+ private Class> contributionClass;
+
+ /**
+ * Get the name of the extension point
+ *
+ * @return The name of the extension point
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of the extension point
+ *
+ * @param name The name of the extension point
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get target bean name of the extension point
+ *
+ * @return The name of the extension point
+ */
+ public String getTargetName() {
+ return targetName;
+ }
+
+ /**
+ * Set the target bean name of the extension point
+ *
+ * @param targetName The target bean name of the extension point
+ */
+ public void setTargetName(String targetName) {
+ this.targetName = targetName;
+ }
+
+ /**
+ * Get target bean of the extension point
+ *
+ * @return The bean of the extension point
+ */
+ public Object getTarget() {
+ return target;
+ }
+
+ /**
+ * Set the target bean of the extension point
+ *
+ * @param target The target bean of the extension point
+ */
+ public void setTarget(Object target) {
+ this.target = target;
+ }
+
+ /**
+ * Get contribution description class of the extension point
+ *
+ * @return The contribution description class of the extension point
+ */
+ public Class> getContributionClass() {
+ return contributionClass;
+ }
+
+ /**
+ * Set contribution description class of the extension point
+ *
+ * @param contributionClass The contribution description class of the extension point
+ */
+ public void setContributionClass(Class> contributionClass) {
+ this.contributionClass = contributionClass;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/component/ComponentLifeCycle.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/component/ComponentLifeCycle.java
new file mode 100644
index 000000000..9d0d30705
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/api/component/ComponentLifeCycle.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.api.component;
+
+import com.alipay.sofa.runtime.api.ServiceRuntimeException;
+
+/**
+ * Interface used to implemented by components who wants to do something when certain lifecycle event of the component.
+ *
+ * @author khotyn
+ * @since 2.6.0
+ */
+public interface ComponentLifeCycle {
+ /**
+ * Component lifecycle event occurred when component is activated.
+ *
+ * @throws ServiceRuntimeException
+ */
+ void activate() throws ServiceRuntimeException;
+
+ /**
+ * Component lifecycle event occurred when component is deactivated.
+ *
+ * @throws ServiceRuntimeException
+ */
+ void deactivate() throws ServiceRuntimeException;
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java
index c6463b31c..e616f7bc2 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/component/impl/ComponentManagerImpl.java
@@ -189,6 +189,25 @@ public Collection getComponentInfosByType(ComponentType type) {
return componentInfos;
}
+ @Override
+ public void resolvePendingResolveComponent(ComponentName componentName) {
+ ComponentInfo componentInfo = registry.get(componentName);
+
+ if (componentInfo.isResolved()) {
+ return;
+ }
+
+ if (componentInfo.resolve()) {
+ typeRegistry(componentInfo);
+ try {
+ componentInfo.activate();
+ } catch (Throwable e) {
+ componentInfo.exception(new Exception(e));
+ SofaLogger.error(e, "Failed to create the component " + componentInfo.getName());
+ }
+ }
+ }
+
private void typeRegistry(ComponentInfo componentInfo) {
ComponentName name = componentInfo.getName();
if (name != null) {
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/client/ExtensionClientImpl.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/client/ExtensionClientImpl.java
new file mode 100644
index 000000000..fe4dc94b3
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/client/ExtensionClientImpl.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.client;
+
+import com.alipay.sofa.runtime.api.client.ExtensionClient;
+import com.alipay.sofa.runtime.api.client.param.ExtensionParam;
+import com.alipay.sofa.runtime.api.client.param.ExtensionPointParam;
+import com.alipay.sofa.runtime.ext.component.ExtensionComponent;
+import com.alipay.sofa.runtime.ext.component.ExtensionImpl;
+import com.alipay.sofa.runtime.ext.component.ExtensionPointComponent;
+import com.alipay.sofa.runtime.ext.component.ExtensionPointImpl;
+import com.alipay.sofa.runtime.spi.component.ComponentInfo;
+import com.alipay.sofa.runtime.spi.component.DefaultImplementation;
+import com.alipay.sofa.runtime.spi.component.Implementation;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
+import com.alipay.sofa.runtime.spi.util.ComponentNameFactory;
+import com.alipay.sofa.service.api.component.ExtensionPoint;
+import org.springframework.util.Assert;
+
+/**
+ * Programming API Implement for Extension/Extension-Point
+ *
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionClientImpl implements ExtensionClient {
+
+ private SofaRuntimeContext sofaRuntimeContext;
+
+ public ExtensionClientImpl(SofaRuntimeContext sofaRuntimeContext) {
+ this.sofaRuntimeContext = sofaRuntimeContext;
+ }
+
+ @Override
+ public void publishExtension(ExtensionParam extensionParam) {
+ Assert.notNull(extensionParam, "extensionParam can not be null.");
+ Assert.notNull(extensionParam.getElement(),
+ "Extension contribution element can not be null.");
+ Assert.notNull(extensionParam.getTargetInstanceName(),
+ "Extension target instance name can not be null.");
+ Assert.notNull(extensionParam.getTargetName(), "Extension target name can not be null.");
+
+ ExtensionImpl extension = new ExtensionImpl(null, extensionParam.getTargetName(),
+ extensionParam.getElement(), sofaRuntimeContext.getAppClassLoader());
+ extension.setTargetComponentName(ComponentNameFactory.createComponentName(
+ ExtensionPointComponent.EXTENSION_POINT_COMPONENT_TYPE,
+ extensionParam.getTargetInstanceName() + ExtensionComponent.LINK_SYMBOL
+ + extensionParam.getTargetName()));
+ ComponentInfo extensionComponent = new ExtensionComponent(extension, sofaRuntimeContext);
+ sofaRuntimeContext.getComponentManager().register(extensionComponent);
+ }
+
+ @Override
+ public void publishExtensionPoint(ExtensionPointParam extensionPointParam) {
+ Assert.notNull(extensionPointParam, "extensionPointParam can not be null.");
+ Assert.notNull(extensionPointParam.getName(), "Extension point name can not be null.");
+ Assert.notNull(extensionPointParam.getContributionClass(),
+ "Extension point contribution can not be null.");
+ Assert.notNull(extensionPointParam.getTarget(), "Extension point target can not be null.");
+
+ ExtensionPoint extensionPoint = new ExtensionPointImpl(extensionPointParam.getName(),
+ extensionPointParam.getContributionClass());
+ Implementation implementation = new DefaultImplementation(
+ extensionPointParam.getTargetName());
+ implementation.setTarget(extensionPointParam.getTarget());
+ ComponentInfo extensionPointComponent = new ExtensionPointComponent(extensionPoint,
+ sofaRuntimeContext, implementation);
+ sofaRuntimeContext.getComponentManager().register(extensionPointComponent);
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionComponent.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionComponent.java
new file mode 100644
index 000000000..f5573fd72
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionComponent.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.component;
+
+import com.alipay.sofa.runtime.api.ServiceRuntimeException;
+import com.alipay.sofa.runtime.api.component.ComponentName;
+import com.alipay.sofa.runtime.api.component.Property;
+import com.alipay.sofa.runtime.model.ComponentStatus;
+import com.alipay.sofa.runtime.model.ComponentType;
+import com.alipay.sofa.runtime.spi.component.AbstractComponent;
+import com.alipay.sofa.runtime.spi.component.ComponentInfo;
+import com.alipay.sofa.runtime.spi.component.ComponentManager;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
+import com.alipay.sofa.runtime.spi.health.HealthResult;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import com.alipay.sofa.runtime.spi.util.ComponentNameFactory;
+import com.alipay.sofa.service.api.component.Extensible;
+import com.alipay.sofa.service.api.component.Extension;
+import com.alipay.sofa.service.api.component.ExtensionPoint;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * SOFA Extension Component
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionComponent extends AbstractComponent {
+ public static final String LINK_SYMBOL = "$";
+ public static final ComponentType EXTENSION_COMPONENT_TYPE = new ComponentType("extension");
+
+ private Extension extension;
+
+ public ExtensionComponent(Extension extension, SofaRuntimeContext sofaRuntimeContext) {
+ this.extension = extension;
+ this.sofaRuntimeContext = sofaRuntimeContext;
+ this.componentName = ComponentNameFactory.createComponentName(
+ EXTENSION_COMPONENT_TYPE,
+ extension.getTargetComponentName().getName() + LINK_SYMBOL
+ + ObjectUtils.getIdentityHexString(extension));
+ }
+
+ @Override
+ public ComponentType getType() {
+ return EXTENSION_COMPONENT_TYPE;
+ }
+
+ @Override
+ public Map getProperties() {
+ return null;
+ }
+
+ @Override
+ public boolean resolve() {
+ if (componentStatus != ComponentStatus.REGISTERED) {
+ return false;
+ }
+
+ ComponentManager componentManager = sofaRuntimeContext.getComponentManager();
+ ComponentName extensionPointComponentName = extension.getTargetComponentName();
+
+ ComponentInfo extensionPointComponentInfo = componentManager
+ .getComponentInfo(extensionPointComponentName);
+
+ if (extensionPointComponentInfo != null && extensionPointComponentInfo.isActivated()) {
+ componentStatus = ComponentStatus.RESOLVED;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void activate() throws ServiceRuntimeException {
+ if (componentStatus != ComponentStatus.RESOLVED) {
+ return;
+ }
+
+ ComponentManager componentManager = sofaRuntimeContext.getComponentManager();
+ ComponentName extensionPointComponentName = extension.getTargetComponentName();
+ ComponentInfo extensionPointComponentInfo = componentManager
+ .getComponentInfo(extensionPointComponentName);
+
+ if (extensionPointComponentInfo == null || !extensionPointComponentInfo.isActivated()) {
+ return;
+ }
+
+ loadContributions(
+ ((ExtensionPointComponent) extensionPointComponentInfo).getExtensionPoint(), extension);
+
+ Object target = extensionPointComponentInfo.getImplementation().getTarget();
+ if (target instanceof Extensible) {
+ try {
+ ((Extensible) target).registerExtension(extension);
+ } catch (Exception e) {
+ throw new ServiceRuntimeException(e);
+ }
+ } else {
+ Method method = ReflectionUtils.findMethod(target.getClass(), "registerExtension",
+ Extension.class);
+ ReflectionUtils.invokeMethod(method, target, extension);
+ }
+
+ componentStatus = ComponentStatus.ACTIVATED;
+ }
+
+ @Override
+ public HealthResult isHealthy() {
+ HealthResult healthResult = new HealthResult(componentName.getRawName());
+ healthResult.setHealthy(true);
+ return healthResult;
+ }
+
+ public Extension getExtension() {
+ return extension;
+ }
+
+ private void loadContributions(ExtensionPoint extensionPoint, Extension extension) {
+ if (extensionPoint != null && extensionPoint.hasContribution()) {
+ try {
+ Object[] contribs = ((ExtensionPointInternal) extensionPoint)
+ .loadContributions((ExtensionInternal) extension);
+ ((ExtensionInternal) extension).setContributions(contribs);
+ } catch (Exception e) {
+ SofaLogger.error(e, "Failed to create contribution objects");
+ }
+ }
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionImpl.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionImpl.java
new file mode 100644
index 000000000..a0a0d20d7
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionImpl.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.component;
+
+import com.alipay.sofa.runtime.api.component.ComponentName;
+import org.w3c.dom.Element;
+
+import java.io.Serializable;
+
+/**
+ * Extension Implement
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+
+public class ExtensionImpl implements ExtensionInternal, Serializable {
+
+ private static final long serialVersionUID = 14648778982899384L;
+ protected ComponentName name;
+ protected ComponentName target;
+ protected String extensionPoint;
+ protected String documentation;
+ protected ClassLoader appClassLoader;
+ protected transient Element element;
+ protected transient Object[] contributions;
+
+ public ExtensionImpl(ComponentName name, String extensionPoint) {
+ this(name, extensionPoint, null, null);
+ }
+
+ public ExtensionImpl(ComponentName name, String extensionPoint, Element element,
+ ClassLoader appClassLoader) {
+ this.name = name;
+ this.extensionPoint = extensionPoint;
+ this.element = element;
+ this.appClassLoader = appClassLoader;
+ }
+
+ public void dispose() {
+ element = null;
+ contributions = null;
+ }
+
+ public Element getElement() {
+ return element;
+ }
+
+ public void setElement(Element element) {
+ this.element = element;
+ }
+
+ public String getExtensionPoint() {
+ return extensionPoint;
+ }
+
+ public ComponentName getComponentName() {
+ return this.name;
+ }
+
+ public ComponentName getTargetComponentName() {
+ return this.target;
+ }
+
+ public void setTargetComponentName(ComponentName target) {
+ this.target = target;
+ }
+
+ public Object[] getContributions() {
+ return contributions;
+ }
+
+ @Override
+ public ClassLoader getAppClassLoader() {
+ return appClassLoader;
+ }
+
+ public void setContributions(Object[] contributions) {
+ this.contributions = contributions;
+ }
+
+ public String getDocumentation() {
+ return documentation;
+ }
+
+ public String getId() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buf = new StringBuilder();
+ buf.append(ExtensionImpl.class.getSimpleName());
+ buf.append(" {");
+ buf.append("target: ");
+ buf.append(name);
+ buf.append(", point:");
+ buf.append(extensionPoint);
+ buf.append('}');
+ return buf.toString();
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionInternal.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionInternal.java
new file mode 100644
index 000000000..af5e8bf12
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionInternal.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.component;
+
+import com.alipay.sofa.runtime.api.component.ComponentName;
+import com.alipay.sofa.service.api.component.Extension;
+import org.w3c.dom.Element;
+
+/**
+ * SOFA Extension Internal Object
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+
+public interface ExtensionInternal extends Extension {
+
+ /**
+ * Set extension element
+ *
+ * @param element extension element
+ */
+ void setElement(Element element);
+
+ /**
+ * Set contributions
+ *
+ * @param contribs contributions
+ */
+ void setContributions(Object[] contribs);
+
+ /**
+ * Set target component(The extension point component) name
+ *
+ * @param target target component name。
+ */
+ void setTargetComponentName(ComponentName target);
+
+ /**
+ * Dispose the component
+ */
+ void dispose();
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointComponent.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointComponent.java
new file mode 100644
index 000000000..20254762b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointComponent.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.component;
+
+import com.alipay.sofa.runtime.api.ServiceRuntimeException;
+import com.alipay.sofa.runtime.api.component.Property;
+import com.alipay.sofa.runtime.model.ComponentType;
+import com.alipay.sofa.runtime.spi.component.AbstractComponent;
+import com.alipay.sofa.runtime.spi.component.ComponentInfo;
+import com.alipay.sofa.runtime.spi.component.ComponentManager;
+import com.alipay.sofa.runtime.spi.component.Implementation;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
+import com.alipay.sofa.runtime.spi.util.ComponentNameFactory;
+import com.alipay.sofa.service.api.component.ExtensionPoint;
+
+import java.util.Map;
+
+/**
+ * SOFA ExtensionPoint Component
+ *
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionPointComponent extends AbstractComponent {
+ private static final String LINK_SYMBOL = "$";
+ public static final ComponentType EXTENSION_POINT_COMPONENT_TYPE = new ComponentType(
+ "extension-point");
+ // Extension
+ private ExtensionPoint extensionPoint;
+
+ public ExtensionPointComponent(ExtensionPoint extensionPoint,
+ SofaRuntimeContext sofaRuntimeContext,
+ Implementation implementation) {
+ this.extensionPoint = extensionPoint;
+ this.sofaRuntimeContext = sofaRuntimeContext;
+ this.implementation = implementation;
+ this.componentName = ComponentNameFactory.createComponentName(
+ EXTENSION_POINT_COMPONENT_TYPE, implementation.getName() + LINK_SYMBOL
+ + this.extensionPoint.getName());
+ }
+
+ @Override
+ public ComponentType getType() {
+ return EXTENSION_POINT_COMPONENT_TYPE;
+ }
+
+ @Override
+ public Map getProperties() {
+ return null;
+ }
+
+ @Override
+ public void activate() throws ServiceRuntimeException {
+ super.activate();
+
+ ComponentManager componentManager = sofaRuntimeContext.getComponentManager();
+
+ for (ComponentInfo componentInfo : componentManager.getComponents()) {
+ if (componentInfo.getType().equals(ExtensionComponent.EXTENSION_COMPONENT_TYPE)
+ && !componentInfo.isResolved()) {
+ ExtensionComponent extensionComponent = (ExtensionComponent) componentInfo;
+ if (extensionComponent.getExtension().getTargetComponentName()
+ .equals(componentName)) {
+ componentManager.resolvePendingResolveComponent(componentInfo.getName());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void deactivate() throws ServiceRuntimeException {
+ ComponentManager componentManager = sofaRuntimeContext.getComponentManager();
+
+ for (ComponentInfo componentInfo : componentManager.getComponents()) {
+ if (componentInfo.getType().equals(ExtensionComponent.EXTENSION_COMPONENT_TYPE)) {
+ ExtensionComponent extensionComponent = (ExtensionComponent) componentInfo;
+ if (extensionComponent.getExtension().getTargetComponentName()
+ .equals(componentName)) {
+ componentManager.unregister(componentInfo);
+ }
+ }
+ }
+ super.deactivate();
+ }
+
+ public ExtensionPoint getExtensionPoint() {
+ return extensionPoint;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointImpl.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointImpl.java
new file mode 100644
index 000000000..8df40e66f
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointImpl.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.component;
+
+import com.alipay.sofa.common.xmap.XMap;
+import com.alipay.sofa.runtime.spi.component.ComponentInfo;
+import org.springframework.util.ClassUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ExtensionPoint Implementation。
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionPointImpl implements ExtensionPointInternal, Serializable {
+ private static final long serialVersionUID = 3939941819263075106L;
+ protected String name;
+ protected String documentation;
+ protected transient List> contributions = new ArrayList>(2);
+ protected transient XMap xmap;
+ protected ClassLoader beanClassLoader;
+
+ public ExtensionPointImpl(String name, Class> contributionClass) {
+ this.name = name;
+ if (contributionClass != null) {
+ this.contributions.add(contributionClass);
+ }
+ }
+
+ public void setBeanClassLoader(ClassLoader beanClassLoader) {
+ this.beanClassLoader = beanClassLoader;
+ }
+
+ public List> getContributions() {
+ return contributions;
+ }
+
+ public boolean hasContribution() {
+ return contributions.size() > 0;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDocumentation() {
+ return documentation;
+ }
+
+ public void addContribution(Class> javaClass) {
+ this.contributions.add(javaClass);
+ }
+
+ public void addContribution(String className) {
+ this.addContribution(ClassUtils.resolveClassName(className, beanClassLoader));
+ }
+
+ public Object[] loadContributions(ExtensionInternal extension) throws Exception {
+ if (contributions != null) {
+ if (xmap == null) {
+ xmap = new XMap();
+ for (Class> contrib : contributions) {
+ xmap.register(contrib);
+ }
+ }
+ Object[] contribs = xmap.loadAll(new XMapContext(extension.getAppClassLoader()),
+ extension.getElement());
+ extension.setContributions(contribs);
+ return contribs;
+ }
+
+ return null;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointInternal.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointInternal.java
new file mode 100644
index 000000000..bccc2b76f
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/ExtensionPointInternal.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.component;
+
+import com.alipay.sofa.service.api.component.ExtensionPoint;
+
+/**
+ * SOFA Extension Point Internal Object
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public interface ExtensionPointInternal extends ExtensionPoint {
+
+ /**
+ * load contributions
+ *
+ * @param extension extension info
+ * @return All contributions
+ * @throws Exception any exception
+ */
+ Object[] loadContributions(ExtensionInternal extension) throws Exception;
+
+ /**
+ * add contribution
+ *
+ * @param className contribution class name
+ */
+ void addContribution(String className);
+
+ /**
+ * add contribution
+ *
+ * @param javaClass contribution class
+ */
+ void addContribution(Class> javaClass);
+
+ /**
+ * set classloader
+ *
+ * @param beanClassLoader bean classloader
+ */
+ void setBeanClassLoader(ClassLoader beanClassLoader);
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/XMapContext.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/XMapContext.java
new file mode 100644
index 000000000..b462de2f1
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/component/XMapContext.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.component;
+
+import com.alipay.sofa.common.xmap.Context;
+
+import java.net.URL;
+
+/**
+ * @author Bogdan Stefanescu
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class XMapContext extends Context {
+
+ private static final long serialVersionUID = -7194560385886298218L;
+
+ private ClassLoader appClassLoader;
+
+ /**
+ *
+ * @param appClassLoader
+ */
+ public XMapContext(ClassLoader appClassLoader) {
+ this.appClassLoader = appClassLoader;
+ }
+
+ /**
+ *
+ * @param className
+ * @return
+ * @throws ClassNotFoundException
+ */
+ @Override
+ public Class> loadClass(String className) throws ClassNotFoundException {
+ return appClassLoader.loadClass(className);
+ }
+
+ /**
+ *
+ * @param name
+ * @return
+ */
+ @Override
+ public URL getResource(String name) {
+ return appClassLoader.getResource(name);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/AbstractExtFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/AbstractExtFactoryBean.java
new file mode 100644
index 000000000..b5b209993
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/AbstractExtFactoryBean.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.core.Ordered;
+
+/**
+ * Common bean for extension and extension point
+ *
+ * @author yangyanzhao@alipay.com
+ * @since 2.6.0
+ */
+public class AbstractExtFactoryBean extends CommonContextBean implements BeanFactoryAware,
+ FactoryBean, Ordered {
+ /* Spring bean context for looking up spring's bean */
+ protected BeanFactory beanFactory;
+
+ protected String targetBeanName;
+
+ protected Object target;
+
+ public final static String LINK_SYMBOL = "$";
+
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+
+ }
+
+ /**
+ * no real bean exist
+ * @return null
+ * @throws Exception any exception
+ */
+ public Object getObject() throws Exception {
+ return null;
+ }
+
+ /**
+ * no real bean exist
+ * @return String.class
+ */
+ public Class> getObjectType() {
+ return String.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
+ }
+
+ /**
+ * Export the given object as an OSGi service. Normally used when the
+ * exported service is a nested bean or an object not managed by the Spring
+ * container.
+ *
+ * @param target The target to set.
+ */
+ public void setTarget(Object target) {
+ this.target = target;
+ }
+
+ public void setTargetBeanName(String name) {
+ this.targetBeanName = name;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/CommonContextBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/CommonContextBean.java
new file mode 100644
index 000000000..45e4e17a2
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/CommonContextBean.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ *
+ * @author linfengqi
+ * @since 2.6.0
+ */
+public class CommonContextBean implements ApplicationContextAware, BeanNameAware, InitializingBean {
+
+ protected String beanName;
+ protected ClassLoader beanClassLoader;
+ protected ConfigurableListableBeanFactory configurableListableBeanFactory;
+ protected SofaRuntimeContext sofaRuntimeContext;
+ protected ApplicationContext applicationContext;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ sofaRuntimeContext = applicationContext.getBean(
+ SofaRuntimeFrameworkConstants.SOFA_RUNTIME_CONTEXT_BEAN_ID, SofaRuntimeContext.class);
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ configurableListableBeanFactory = (ConfigurableListableBeanFactory) applicationContext
+ .getAutowireCapableBeanFactory();
+ }
+
+ public void setBeanClassLoader(ClassLoader beanClassLoader) {
+ this.beanClassLoader = beanClassLoader;
+ }
+
+ public ClassLoader getBeanClassLoader() {
+ return beanClassLoader;
+ }
+
+ public void setBeanName(String beanName) {
+ this.beanName = beanName;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionBuilder.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionBuilder.java
new file mode 100644
index 000000000..ca0bad791
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionBuilder.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import com.alipay.sofa.runtime.api.component.ComponentName;
+import com.alipay.sofa.runtime.ext.component.ExtensionInternal;
+import com.alipay.sofa.service.api.component.Extension;
+import org.springframework.context.ApplicationContext;
+import org.w3c.dom.Element;
+
+/**
+ * Extension Builder
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+
+public class ExtensionBuilder {
+
+ private ExtensionInternal extensionInternal;
+
+ private ExtensionBuilder() {
+
+ }
+
+ /**
+ * Get extension
+ * @return extension
+ */
+ public Extension getExtension() {
+ return this.extensionInternal;
+ }
+
+ /**
+ * create extension builder
+ *
+ * @param extensionPoint extension point name
+ * @param element element
+ * @param applicationContext application context
+ * @return extension builder
+ */
+ public static ExtensionBuilder genericExtension(String extensionPoint, Element element,
+ ApplicationContext applicationContext,
+ ClassLoader appClassLoader) {
+ ExtensionBuilder builder = new ExtensionBuilder();
+ builder.extensionInternal = new SpringExtensionImpl(null, extensionPoint, element,
+ appClassLoader, applicationContext);
+ return builder;
+ }
+
+ /**
+ * Set element
+ *
+ * @param element element of the extension
+ */
+ public void setElement(Element element) {
+ this.extensionInternal.setElement(element);
+ }
+
+ /**
+ * Set target component name
+ *
+ * @param target target component name
+ */
+ public void setExtensionPoint(ComponentName target) {
+ this.extensionInternal.setTargetComponentName(target);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionFactoryBean.java
new file mode 100644
index 000000000..f96894519
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionFactoryBean.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import com.alipay.sofa.runtime.api.component.ComponentName;
+import com.alipay.sofa.runtime.ext.component.ExtensionComponent;
+import com.alipay.sofa.runtime.ext.component.ExtensionPointComponent;
+import com.alipay.sofa.runtime.spi.component.ComponentInfo;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import com.alipay.sofa.runtime.spi.util.ComponentNameFactory;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @author yangyanzhao@alipay.com
+ * @author khotyn
+ * @since 2.6.0
+ */
+public class ExtensionFactoryBean extends AbstractExtFactoryBean {
+
+ /* extension bean */
+ private String bean;
+
+ /* extension point name */
+ private String point;
+
+ /* content need to be parsed with XMap */
+ private Element content;
+
+ private String[] require;
+
+ private ClassLoader classLoader;
+
+ public void afterPropertiesSet() throws Exception {
+ super.afterPropertiesSet();
+ Assert.notNull(applicationContext,
+ "required property 'applicationContext' has not been set");
+ Assert.notNull(getPoint(), "required property 'point' has not been set for extension");
+ // checked
+ if (!StringUtils.hasText(getPoint())) {
+ throw new IllegalArgumentException("'point' have to be specified");
+ }
+
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ try {
+ publishAsNuxeoExtension();
+ } catch (Exception e) {
+ SofaLogger.error(e, "failed to publish extension");
+ throw e;
+ }
+ }
+
+ private void publishAsNuxeoExtension() throws Exception {
+ ExtensionBuilder extensionBuilder = ExtensionBuilder.genericExtension(this.getPoint(),
+ this.getContent(), applicationContext, classLoader);
+ extensionBuilder.setExtensionPoint(getExtensionPointComponentName());
+
+ ComponentInfo componentInfo = new ExtensionComponent(extensionBuilder.getExtension(),
+ sofaRuntimeContext);
+ sofaRuntimeContext.getComponentManager().register(componentInfo);
+ }
+
+ private ComponentName getExtensionPointComponentName() {
+ return ComponentNameFactory.createComponentName(
+ ExtensionPointComponent.EXTENSION_POINT_COMPONENT_TYPE, this.getBean() + LINK_SYMBOL
+ + this.getPoint());
+ }
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public void setContribution(String[] contribution) throws Exception {
+ if (contribution != null && contribution.length != 0) {
+ Class>[] contrClass = new Class[contribution.length];
+ int i = 0;
+ for (String cntr : contribution) {
+ contrClass[i] = classLoader.loadClass(cntr);
+ i++;
+ }
+ }
+ }
+
+ public void setRequire(String[] require) {
+ this.require = require;
+ }
+
+ public String[] getRequire() {
+ return require;
+ }
+
+ public void setBean(String bean) {
+ this.bean = bean;
+ }
+
+ public String getBean() {
+ return bean;
+ }
+
+ public void setContent(Element content) {
+ this.content = content;
+ }
+
+ public Element getContent() {
+ return content;
+ }
+
+ public void setPoint(String point) {
+ this.point = point;
+ }
+
+ public String getPoint() {
+ return point;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionPointBuilder.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionPointBuilder.java
new file mode 100644
index 000000000..a2b6e04f9
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionPointBuilder.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import com.alipay.sofa.runtime.ext.component.ExtensionPointInternal;
+import com.alipay.sofa.service.api.component.ExtensionPoint;
+
+/**
+ * Extension point builder
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class ExtensionPointBuilder {
+
+ private ExtensionPointInternal extensionPointInternal;
+
+ private ExtensionPointBuilder() {
+
+ }
+
+ /**
+ * Get extension point
+ * @return extension point
+ */
+ public ExtensionPoint getExtensionPoint() {
+ return this.extensionPointInternal;
+ }
+
+ /**
+ * Create extension point builder
+ * @param name extension point name
+ * @param beanClassLoader classloader
+ * @return extension point builder
+ */
+ public static ExtensionPointBuilder genericExtensionPoint(String name,
+ ClassLoader beanClassLoader) {
+ return genericExtensionPoint(name, null, beanClassLoader);
+ }
+
+ /**
+ * Create extension point builder
+ * @param name extension point name
+ * @param contributionClass contribution class
+ * @param beanClassLoader classloader
+ * @return extension point builder
+ */
+ public static ExtensionPointBuilder genericExtensionPoint(String name,
+ Class> contributionClass,
+ ClassLoader beanClassLoader) {
+ ExtensionPointBuilder builder = new ExtensionPointBuilder();
+
+ ExtensionPointInternal extensionPoint = new SpringExtensionPointImpl(name,
+ contributionClass);
+ extensionPoint.setBeanClassLoader(beanClassLoader);
+
+ builder.extensionPointInternal = extensionPoint;
+ return builder;
+ }
+
+ /**
+ * add contribution to extension point
+ * @param javaClass contribution class
+ */
+ public void addContribution(Class> javaClass) {
+ this.extensionPointInternal.addContribution(javaClass);
+ }
+
+ /**
+ * add contribution to extension point
+ * @param className contribution class name
+ */
+ public void addContribution(String className) {
+ this.extensionPointInternal.addContribution(className);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionPointFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionPointFactoryBean.java
new file mode 100644
index 000000000..532d43036
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/ExtensionPointFactoryBean.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import com.alipay.sofa.runtime.ext.component.ExtensionPointComponent;
+import com.alipay.sofa.runtime.spi.component.ComponentInfo;
+import com.alipay.sofa.runtime.spi.component.Implementation;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import com.alipay.sofa.runtime.spi.spring.SpringImplementationImpl;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Extension point factory bean
+ *
+ * @author xi.hux@alipay.com
+ * @author yangyanzhao@alipay.com
+ * @since 2.6.0
+ */
+public class ExtensionPointFactoryBean extends AbstractExtFactoryBean {
+
+ /* extension point name */
+ private String name;
+
+ /* contributions for the extension point */
+ private String[] contribution;
+
+ public void afterPropertiesSet() throws Exception {
+ super.afterPropertiesSet();
+ Assert.notNull(beanFactory, "required property 'beanFactory' has not been set");
+ Assert.notNull(name, "required property 'name' has not been set for extension point");
+
+ if (!StringUtils.hasText(targetBeanName) && target == null
+ || (StringUtils.hasText(targetBeanName) && target != null)) {
+ throw new IllegalArgumentException(
+ "either 'target' or 'targetBeanName' have to be specified");
+ }
+
+ // determine serviceClass (can still be null if using a FactoryBean
+ // which doesn't declare its product type)
+ Class> extensionPointClass = (target != null ? target.getClass() : beanFactory
+ .getType(targetBeanName));
+
+ // check if there is a reference to a non-lazy bean
+ if (StringUtils.hasText(targetBeanName)) {
+ if (beanFactory instanceof ConfigurableListableBeanFactory) {
+ // in case the target is non-lazy, singleton bean, initialize it
+ BeanDefinition beanDef = ((ConfigurableListableBeanFactory) beanFactory)
+ .getBeanDefinition(targetBeanName);
+
+ if (beanDef.isSingleton() && !beanDef.isLazyInit()) {
+ if (SofaLogger.isDebugEnabled()) {
+ SofaLogger
+ .debug("target bean ["
+ + targetBeanName
+ + "] is a non-lazy singleton; forcing initialization before publishing");
+ }
+ beanFactory.getBean(targetBeanName);
+ }
+ }
+ }
+
+ try {
+ publishAsNuxeoExtensionPoint(extensionPointClass);
+ } catch (Exception e) {
+ SofaLogger.error(e, "Failed to publish extension point.");
+ throw e;
+ }
+ }
+
+ private void publishAsNuxeoExtensionPoint(Class> beanClass) throws Exception {
+ Assert.notNull(beanClass, "Service must be implement!");
+
+ ExtensionPointBuilder extensionPointBuilder = ExtensionPointBuilder.genericExtensionPoint(
+ this.name, applicationContext.getClassLoader());
+
+ if (this.contribution != null && this.contribution.length != 0) {
+ for (int i = 0; i < contribution.length; i++) {
+ extensionPointBuilder.addContribution(contribution[i]);
+ }
+ }
+ Assert.hasLength(beanName,
+ "required property 'beanName' has not been set for creating implementation");
+ Assert.notNull(applicationContext,
+ "required property 'applicationContext' has not been set for creating implementation");
+ Implementation implementation = new SpringImplementationImpl(targetBeanName,
+ applicationContext);
+ ComponentInfo extensionPointComponent = new ExtensionPointComponent(
+ extensionPointBuilder.getExtensionPoint(), sofaRuntimeContext, implementation);
+ sofaRuntimeContext.getComponentManager().register(extensionPointComponent);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setContribution(String[] contribution) throws Exception {
+ this.contribution = contribution;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/SpringExtensionImpl.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/SpringExtensionImpl.java
new file mode 100644
index 000000000..fdf58dc3b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/SpringExtensionImpl.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import com.alipay.sofa.runtime.api.component.ComponentName;
+import com.alipay.sofa.runtime.ext.component.ExtensionImpl;
+import org.springframework.context.ApplicationContext;
+import org.w3c.dom.Element;
+
+/**
+ * Extension implement in spring env
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+
+public class SpringExtensionImpl extends ExtensionImpl {
+
+ /**
+ * Comment for serialVersionUID
+ */
+ private static final long serialVersionUID = 1574173210970111642L;
+
+ protected ApplicationContext applicationContext;
+
+ public SpringExtensionImpl(ComponentName name, String extensionPoint, Element element,
+ ClassLoader appClassLoader, ApplicationContext applicationContext) {
+ super(name, extensionPoint, element, appClassLoader);
+ this.applicationContext = applicationContext;
+ }
+
+ public ApplicationContext getApplicationContext() {
+ return this.applicationContext;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/SpringExtensionPointImpl.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/SpringExtensionPointImpl.java
new file mode 100644
index 000000000..c29b29b9a
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/SpringExtensionPointImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring;
+
+import com.alipay.sofa.common.xmap.annotation.spring.XMapSpring;
+import com.alipay.sofa.runtime.ext.component.ExtensionInternal;
+import com.alipay.sofa.runtime.ext.component.ExtensionPointImpl;
+import com.alipay.sofa.runtime.ext.component.XMapContext;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * Extension point implement in spring env
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+
+public class SpringExtensionPointImpl extends ExtensionPointImpl {
+
+ private static final long serialVersionUID = -7891847787889605861L;
+
+ private XMapSpring xmapSpring;
+
+ public SpringExtensionPointImpl(String name, Class> contributionClass) {
+ super(name, contributionClass);
+ }
+
+ @Override
+ public Object[] loadContributions(ExtensionInternal extension) throws Exception {
+
+ ApplicationContext applicationContext = null;
+
+ if (extension instanceof SpringExtensionImpl) {
+ applicationContext = ((SpringExtensionImpl) extension).getApplicationContext();
+ }
+ if (contributions != null) {
+ xmapSpring = new XMapSpring();
+ for (Class> contrib : contributions) {
+ xmapSpring.register(contrib, applicationContext);
+ }
+
+ Object[] contribs = xmapSpring.loadAll(new XMapContext(extension.getAppClassLoader()),
+ extension.getElement());
+ for (Object o : contribs) {
+ if (applicationContext != null && o instanceof BeanFactoryAware) {
+ ((BeanFactoryAware) o).setBeanFactory(applicationContext
+ .getAutowireCapableBeanFactory());
+ }
+ }
+ extension.setContributions(contribs);
+
+ return contribs;
+ }
+ return null;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractExtBeanDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractExtBeanDefinitionParser.java
new file mode 100644
index 000000000..1bb1c118b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractExtBeanDefinitionParser.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring.parser;
+
+import com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport;
+import com.alipay.sofa.runtime.ext.spring.parser.AbstractSingleExtPointBeanDefinitionParser;
+import com.alipay.sofa.runtime.spi.util.ParserUtils;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.core.Conventions;
+import org.springframework.core.io.Resource;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+/**
+ * Common parser for extension and extension point
+ *
+ * @author yangyanzhao@alipay.com
+ * @author yangyanzhao
+ * @since 2.6.0
+ */
+public abstract class AbstractExtBeanDefinitionParser extends
+ AbstractSingleExtPointBeanDefinitionParser
+ implements
+ SofaBootTagNameSupport {
+ public static final String REF = "ref";
+
+ private static final String BEAN_CLASS_LOADER = "beanClassLoader";
+
+ /**
+ *
+ * @param element the XML element being parsed
+ * @param parserContext the object encapsulating the current state of the parsing process
+ * @param builder used to define the BeanDefinition
+ */
+ @Override
+ protected void doParse(Element element, ParserContext parserContext,
+ BeanDefinitionBuilder builder) {
+ Resource res = parserContext.getReaderContext().getResource();
+
+ builder.getBeanDefinition().setResource(res);
+ builder.getRawBeanDefinition().setResource(res);
+
+ configBeanClassLoader(parserContext, builder);
+
+ // parse attributes
+ parseAttribute(element, parserContext, builder);
+
+ // parser subElement(i.e. )
+ parserSubElement(element, parserContext, builder);
+ }
+
+ protected void configBeanClassLoader(ParserContext parserContext, BeanDefinitionBuilder builder) {
+ ClassLoader beanClassLoader = parserContext.getReaderContext().getBeanClassLoader();
+ builder.addPropertyValue(BEAN_CLASS_LOADER, beanClassLoader);
+ }
+
+ protected void parseAttribute(Element element, ParserContext parserContext,
+ BeanDefinitionBuilder builder) {
+ ParserUtils.parseCustomAttributes(element, parserContext, builder,
+ new ParserUtils.AttributeCallback() {
+
+ public void process(Element parent, Attr attribute, BeanDefinitionBuilder builder,
+ ParserContext parserContext) {
+ String name = attribute.getLocalName();
+
+ // fallback mechanism
+ if (!REF.equals(name)) {
+ builder.addPropertyValue(Conventions.attributeNameToPropertyName(name),
+ attribute.getValue());
+ }
+
+ }
+ });
+ }
+
+ protected abstract void parserSubElement(Element element, ParserContext parserContext,
+ BeanDefinitionBuilder builder);
+
+ protected boolean shouldGenerateIdAsFallback() {
+ return true;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractExtPointBeanDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractExtPointBeanDefinitionParser.java
new file mode 100644
index 000000000..577a99d1e
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractExtPointBeanDefinitionParser.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring.parser;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+
+/**
+ * @author fengqi.lin
+ * @since 2.6.0
+ */
+public abstract class AbstractExtPointBeanDefinitionParser implements BeanDefinitionParser {
+
+ /**
+ * Constant for the id attribute
+ */
+ public static final String ID_ATTRIBUTE = "id";
+
+ /**
+ *
+ * @param element
+ * @param parserContext
+ * @return
+ */
+ public final BeanDefinition parse(Element element, ParserContext parserContext) {
+ AbstractBeanDefinition definition = parseInternal(element, parserContext);
+ if (definition != null && !parserContext.isNested()) {
+ try {
+ String id = resolveId(element, definition, parserContext);
+ if (!StringUtils.hasText(id)) {
+ parserContext.getReaderContext().error(
+ "Id is required for element '"
+ + parserContext.getDelegate().getLocalName(element)
+ + "' when used as a top-level tag", element);
+ }
+ BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id);
+ registerBeanDefinition(holder, parserContext.getRegistry());
+ if (shouldFireEvents()) {
+ BeanComponentDefinition componentDefinition = new BeanComponentDefinition(
+ holder);
+ postProcessComponentDefinition(componentDefinition);
+ parserContext.registerComponent(componentDefinition);
+ }
+ } catch (BeanDefinitionStoreException ex) {
+ parserContext.getReaderContext().error(ex.getMessage(), element);
+ return null;
+ }
+ }
+ return definition;
+ }
+
+ /**
+ * Resolve the ID for the supplied {@link BeanDefinition}.
+ * When using {@link #shouldGenerateId generation}, a name is generated automatically.
+ * Otherwise, the ID is extracted from the "id" attribute, potentially with a
+ * {@link #shouldGenerateIdAsFallback() fallback} to a generated id.
+ *
+ * @param element the element that the bean definition has been built from
+ * @param definition the bean definition to be registered
+ * @param parserContext the object encapsulating the current state of the parsing process;
+ * provides access to a {@link BeanDefinitionRegistry}
+ * @return the resolved id
+ * @throws BeanDefinitionStoreException if no unique name could be generated
+ * for the given bean definition
+ */
+ protected String resolveId(Element element, AbstractBeanDefinition definition,
+ ParserContext parserContext) throws BeanDefinitionStoreException {
+
+ if (shouldGenerateId()) {
+ return parserContext.getReaderContext().generateBeanName(definition);
+ } else {
+ String id = element.getAttribute(ID_ATTRIBUTE);
+ if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
+ id = parserContext.getReaderContext().generateBeanName(definition);
+ }
+ return id;
+ }
+ }
+
+ /**
+ * Register the supplied {@link BeanDefinitionHolder bean} with the supplied
+ * {@link BeanDefinitionRegistry registry}.
+ *
Subclasses can override this method to control whether or not the supplied
+ * {@link BeanDefinitionHolder bean} is actually even registered, or to
+ * register even more beans.
+ *
The default implementation registers the supplied {@link BeanDefinitionHolder bean}
+ * with the supplied {@link BeanDefinitionRegistry registry} only if the isNested
+ * parameter is false
, because one typically does not want inner beans
+ * to be registered as top level beans.
+ *
+ * @param definition the bean definition to be registered
+ * @param registry the registry that the bean is to be registered with
+ * @see BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry)
+ */
+ protected void registerBeanDefinition(BeanDefinitionHolder definition,
+ BeanDefinitionRegistry registry) {
+ BeanDefinitionReaderUtils.registerBeanDefinition(definition, registry);
+ }
+
+ /**
+ * Central template method to actually parse the supplied {@link Element}
+ * into one or more {@link BeanDefinition BeanDefinitions}.
+ *
+ * @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
+ * @param parserContext the object encapsulating the current state of the parsing process;
+ * provides access to a {@link BeanDefinitionRegistry}
+ * @return the primary {@link BeanDefinition} resulting from the parsing of the supplied {@link Element}
+ * @see #parse(Element, ParserContext)
+ * @see #postProcessComponentDefinition(BeanComponentDefinition)
+ */
+ protected abstract AbstractBeanDefinition parseInternal(Element element,
+ ParserContext parserContext);
+
+ /**
+ * Should an ID be generated instead of read from the passed in {@link Element}?
+ *
Disabled by default; subclasses can override this to enable ID generation.
+ * Note that this flag is about always generating an ID; the parser
+ * won't even check for an "id" attribute in this case.
+ *
+ * @return whether the parser should always generate an id
+ */
+ protected boolean shouldGenerateId() {
+ return false;
+ }
+
+ /**
+ * Should an ID be generated instead if the passed in {@link Element} does not
+ * specify an "id" attribute explicitly?
+ *
Disabled by default; subclasses can override this to enable ID generation
+ * as fallback: The parser will first check for an "id" attribute in this case,
+ * only falling back to a generated ID if no value was specified.
+ *
+ * @return whether the parser should generate an id if no id was specified
+ */
+ protected boolean shouldGenerateIdAsFallback() {
+ return false;
+ }
+
+ /**
+ * Controls whether this parser is supposed to fire a
+ * {@link BeanComponentDefinition}
+ * event after parsing the bean definition.
+ *
This implementation returns true
by default; that is,
+ * an event will be fired when a bean definition has been completely parsed.
+ * Override this to return false
in order to suppress the event.
+ *
+ * @return true
in order to fire a component registration event
+ * after parsing the bean definition; false
to suppress the event
+ * @see #postProcessComponentDefinition
+ * @see org.springframework.beans.factory.parsing.ReaderContext#fireComponentRegistered
+ */
+ protected boolean shouldFireEvents() {
+ return true;
+ }
+
+ /**
+ * Hook method called after the primary parsing of a
+ * {@link BeanComponentDefinition} but before the
+ * {@link BeanComponentDefinition} has been registered with a
+ * {@link BeanDefinitionRegistry}.
+ *
Derived classes can override this method to supply any custom logic that
+ * is to be executed after all the parsing is finished.
+ *
The default implementation is a no-op.
+ *
+ * @param componentDefinition the {@link BeanComponentDefinition} that is to be processed
+ */
+ protected void postProcessComponentDefinition(BeanComponentDefinition componentDefinition) {
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractSingleExtPointBeanDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractSingleExtPointBeanDefinitionParser.java
new file mode 100644
index 000000000..497530abf
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/AbstractSingleExtPointBeanDefinitionParser.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring.parser;
+
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+/**
+ * @author fengqi.lin
+ * @since 2.6.0
+ */
+public abstract class AbstractSingleExtPointBeanDefinitionParser extends
+ AbstractExtPointBeanDefinitionParser {
+
+ /**
+ * Creates a {@link BeanDefinitionBuilder} instance for the
+ * {@link #getBeanClass bean Class} and passes it to the
+ * {@link #doParse} strategy method.
+ *
+ * @param element the element that is to be parsed into a single BeanDefinition
+ * @param parserContext the object encapsulating the current state of the parsing process
+ * @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
+ * @throws IllegalStateException if the bean {@link Class} returned from
+ * {@link #getBeanClass(Element)} is null
+ * @see #doParse
+ */
+ @Override
+ protected final AbstractBeanDefinition parseInternal(Element element,
+ ParserContext parserContext) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
+ String parentName = getParentName(element);
+ if (parentName != null) {
+ builder.getRawBeanDefinition().setParentName(parentName);
+ }
+ Class beanClass = getBeanClass(element);
+ if (beanClass != null) {
+ builder.getRawBeanDefinition().setBeanClass(beanClass);
+ } else {
+ String beanClassName = getBeanClassName(element);
+ if (beanClassName != null) {
+ builder.getRawBeanDefinition().setBeanClassName(beanClassName);
+ }
+ }
+ builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
+ if (parserContext.isNested()) {
+ // Inner bean definition must receive same scope as containing bean.
+ builder.setScope(parserContext.getContainingBeanDefinition().getScope());
+ }
+ if (parserContext.isDefaultLazyInit()) {
+ // Default-lazy-init applies to custom bean definitions as well.
+ builder.setLazyInit(true);
+ }
+ doParse(element, parserContext, builder);
+ return builder.getBeanDefinition();
+ }
+
+ /**
+ * Determine the name for the parent of the currently parsed bean,
+ * in case of the current bean being defined as a child bean.
+ *
The default implementation returns null
,
+ * indicating a root bean definition.
+ *
+ * @param element the Element
that is being parsed
+ * @return the name of the parent bean for the currently parsed bean,
+ * or null
if none
+ */
+ protected String getParentName(Element element) {
+ return null;
+ }
+
+ /**
+ * Determine the bean class corresponding to the supplied {@link Element}.
+ *
Note that, for application classes, it is generally preferable to
+ * override {@link #getBeanClassName} instead, in order to avoid a direct
+ * dependence on the bean implementation class. The BeanDefinitionParser
+ * and its NamespaceHandler can be used within an IDE plugin then, even
+ * if the application classes are not available on the plugin's classpath.
+ *
+ * @param element the Element
that is being parsed
+ * @return the {@link Class} of the bean that is being defined via parsing
+ * the supplied Element
, or null
if none
+ * @see #getBeanClassName
+ */
+ protected Class getBeanClass(Element element) {
+ return null;
+ }
+
+ /**
+ * Determine the bean class name corresponding to the supplied {@link Element}.
+ *
+ * @param element the Element
that is being parsed
+ * @return the class name of the bean that is being defined via parsing
+ * the supplied Element
, or null
if none
+ * @see #getBeanClass
+ */
+ protected String getBeanClassName(Element element) {
+ return null;
+ }
+
+ /**
+ * Parse the supplied {@link Element} and populate the supplied
+ * {@link BeanDefinitionBuilder} as required.
+ *
The default implementation delegates to the doParse
+ * version without ParserContext argument.
+ *
+ * @param element the XML element being parsed
+ * @param parserContext the object encapsulating the current state of the parsing process
+ * @param builder used to define the BeanDefinition
+ * @see #doParse(Element, BeanDefinitionBuilder)
+ */
+ protected void doParse(Element element, ParserContext parserContext,
+ BeanDefinitionBuilder builder) {
+ doParse(element, builder);
+ }
+
+ /**
+ * Parse the supplied {@link Element} and populate the supplied
+ * {@link BeanDefinitionBuilder} as required.
+ *
The default implementation does nothing.
+ *
+ * @param element the XML element being parsed
+ * @param builder used to define the BeanDefinition
+ */
+ protected void doParse(Element element, BeanDefinitionBuilder builder) {
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/ExtensionBeanDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/ExtensionBeanDefinitionParser.java
new file mode 100644
index 000000000..a685ac2f6
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/ExtensionBeanDefinitionParser.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring.parser;
+
+import com.alipay.sofa.runtime.ext.spring.ExtensionFactoryBean;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Extension definition parser
+ *
+ * @author xi.hux@alipay.com
+ * @author yangyanzhao@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionBeanDefinitionParser extends AbstractExtBeanDefinitionParser {
+
+ public static final String CONTENT = "content";
+
+ private static final ExtensionBeanDefinitionParser instance = new ExtensionBeanDefinitionParser();
+
+ public ExtensionBeanDefinitionParser() {
+ }
+
+ public static ExtensionBeanDefinitionParser getInstance() {
+ return instance;
+ }
+
+ @Override
+ protected Class> getBeanClass(Element element) {
+ return ExtensionFactoryBean.class;
+ }
+
+ @Override
+ protected void parserSubElement(Element element, ParserContext parserContext,
+ BeanDefinitionBuilder builder) {
+ NodeList nl = element.getChildNodes();
+
+ // parse all sub elements
+ for (int i = 0; i < nl.getLength(); i++) {
+ Node node = nl.item(i);
+ if (node instanceof Element) {
+ Element subElement = (Element) node;
+ // osgi:content
+ if (CONTENT.equals(subElement.getLocalName())) {
+ builder.addPropertyValue(CONTENT, subElement);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String supportTagName() {
+ return "extension";
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/ExtensionPointBeanDefinitionParser.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/ExtensionPointBeanDefinitionParser.java
new file mode 100644
index 000000000..52e1d4d91
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/ext/spring/parser/ExtensionPointBeanDefinitionParser.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.ext.spring.parser;
+
+import com.alipay.sofa.runtime.ext.spring.ExtensionPointFactoryBean;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import com.alipay.sofa.runtime.spi.util.ParserUtils;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.core.Conventions;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Extension point definition parser
+ *
+ * @author xi.hux@alipay.com
+ * @author yangyanzhao@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionPointBeanDefinitionParser extends AbstractExtBeanDefinitionParser {
+
+ public static final String CLASS = "class";
+
+ public static final String OBJECT = "object";
+
+ public static final String CONTRIBUTION = "contribution";
+
+ private static final ExtensionPointBeanDefinitionParser instance = new ExtensionPointBeanDefinitionParser();
+
+ public ExtensionPointBeanDefinitionParser() {
+ }
+
+ public static ExtensionPointBeanDefinitionParser getInstance() {
+ return instance;
+ }
+
+ @Override
+ protected Class> getBeanClass(Element element) {
+ return ExtensionPointFactoryBean.class;
+ }
+
+ @Override
+ protected void parserSubElement(Element element, ParserContext parserContext,
+ BeanDefinitionBuilder builder) {
+ // determine nested/referred beans
+ // only referred bean is supported now
+ Object target = null;
+ if (element.hasAttribute(REF)) {
+ target = new RuntimeBeanReference(element.getAttribute(REF));
+ }
+
+ NodeList nl = element.getChildNodes();
+ final Set contributions = new LinkedHashSet();
+ // parse all sub elements
+ for (int i = 0; i < nl.getLength(); i++) {
+ Node node = nl.item(i);
+ if (node instanceof Element) {
+ Element subElement = (Element) node;
+ // sofa:object
+ if (OBJECT.equals(subElement.getLocalName())) {
+ ParserUtils.parseCustomAttributes(subElement, parserContext, builder,
+ new ParserUtils.AttributeCallback() {
+
+ public void process(Element parent, Attr attribute,
+ BeanDefinitionBuilder builder,
+ ParserContext parserContext) {
+
+ String name = attribute.getLocalName();
+ if (CLASS.equals(name)) {
+ contributions.add(attribute.getValue());
+ } else {
+ builder.addPropertyValue(
+ Conventions.attributeNameToPropertyName(name),
+ attribute.getValue());
+ }
+ }
+ });
+ } else {
+ if (element.hasAttribute(REF)) {
+ SofaLogger
+ .error("nested bean definition/reference cannot be used when attribute 'ref' is specified");
+ }
+ target = parserContext.getDelegate().parsePropertySubElement(subElement,
+ builder.getBeanDefinition());
+ }
+ }
+ }
+ builder.addPropertyValue(CONTRIBUTION, contributions);
+ // do we have a bean reference ?
+ if (target instanceof RuntimeBeanReference) {
+ builder.addPropertyValue("targetBeanName",
+ ((RuntimeBeanReference) target).getBeanName());
+ }
+ // or a nested bean? -- not supported yet
+ else {
+ builder.addPropertyValue("target", target);
+ }
+ }
+
+ @Override
+ public String supportTagName() {
+ return "extension-point";
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/AbstractComponent.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/AbstractComponent.java
index bc137ab21..ccf83d35a 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/AbstractComponent.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/AbstractComponent.java
@@ -17,6 +17,7 @@
package com.alipay.sofa.runtime.spi.component;
import com.alipay.sofa.runtime.api.ServiceRuntimeException;
+import com.alipay.sofa.runtime.api.component.ComponentLifeCycle;
import com.alipay.sofa.runtime.api.component.ComponentName;
import com.alipay.sofa.runtime.model.ComponentStatus;
import com.alipay.sofa.runtime.spi.health.HealthResult;
@@ -121,6 +122,13 @@ public void activate() throws ServiceRuntimeException {
return;
}
+ if (this.implementation != null) {
+ Object target = this.implementation.getTarget();
+ if (target instanceof ComponentLifeCycle) {
+ ((ComponentLifeCycle) target).activate();
+ }
+ }
+
componentStatus = ComponentStatus.ACTIVATED;
}
@@ -134,6 +142,13 @@ public void deactivate() throws ServiceRuntimeException {
return;
}
+ if (this.implementation != null) {
+ Object target = this.implementation.getTarget();
+ if (target instanceof ComponentLifeCycle) {
+ ((ComponentLifeCycle) target).deactivate();
+ }
+ }
+
componentStatus = ComponentStatus.RESOLVED;
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/ComponentManager.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/ComponentManager.java
index 15a96e7df..96fdd8cf4 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/ComponentManager.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/component/ComponentManager.java
@@ -106,4 +106,11 @@ public interface ComponentManager {
* @return components
*/
Collection getComponentInfosByType(ComponentType type);
+
+ /**
+ * resolve pending component
+ *
+ * @param componentName component name
+ */
+ void resolvePendingResolveComponent(ComponentName componentName);
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/log/SofaLogger.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/log/SofaLogger.java
index 2875c4304..1bc3fcb17 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/log/SofaLogger.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/log/SofaLogger.java
@@ -45,6 +45,10 @@ public static void warn(String format, Object... args) {
}
}
+ public static void error(String msg, Throwable throwable) {
+ DEFAULT_LOG.error(msg, throwable);
+ }
+
public static void error(String format, Object... args) {
DEFAULT_LOG.error(getMessage(format, args));
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/spring/SpringImplementationImpl.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/spring/SpringImplementationImpl.java
new file mode 100644
index 000000000..4d788d5f6
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/spring/SpringImplementationImpl.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spi.spring;
+
+import com.alipay.sofa.runtime.spi.component.DefaultImplementation;
+import org.springframework.context.ApplicationContext;
+import org.springframework.util.Assert;
+
+/**
+ * Spring Component Implement
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+
+public class SpringImplementationImpl extends DefaultImplementation {
+
+ protected ApplicationContext applicationContext;
+ protected String beanName;
+ protected Object target;
+
+ public SpringImplementationImpl(String beanName, ApplicationContext applicationContext) {
+ Assert.hasText(beanName, "beanName must not be empty");
+ Assert.notNull(applicationContext, "applicationContext must not be null");
+
+ this.beanName = beanName;
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public Object getTarget() {
+ return applicationContext.getBean(this.beanName);
+ }
+
+ @Override
+ public Class> getTargetClass() {
+ return applicationContext.getBean(this.beanName).getClass();
+ }
+
+ @Override
+ public void setTarget(Object target) {
+ this.target = target;
+ }
+
+ @Override
+ public String getName() {
+ return beanName;
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/util/ParserUtils.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/util/ParserUtils.java
new file mode 100644
index 000000000..b5ebfeae2
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spi/util/ParserUtils.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spi.util;
+
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+public class ParserUtils {
+
+ /**
+ * Parse custom attributes, as ID, LAZY-INIT, DEPENDS-ON。
+ *
+ * @param element element
+ * @param parserContext parser context
+ * @param builder builder
+ * @param callback callback
+ */
+ public static void parseCustomAttributes(Element element, ParserContext parserContext,
+ BeanDefinitionBuilder builder,
+ AttributeCallback callback) {
+ NamedNodeMap attributes = element.getAttributes();
+
+ for (int x = 0; x < attributes.getLength(); x++) {
+ Attr attribute = (Attr) attributes.item(x);
+ String name = attribute.getLocalName();
+
+ if (BeanDefinitionParserDelegate.DEPENDS_ON_ATTRIBUTE.equals(name)) {
+ builder.getBeanDefinition().setDependsOn(
+ (StringUtils.tokenizeToStringArray(attribute.getValue(),
+ BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS)));
+ } else if (BeanDefinitionParserDelegate.LAZY_INIT_ATTRIBUTE.equals(name)) {
+ builder.setLazyInit(Boolean.getBoolean(attribute.getValue()));
+ } else if (BeanDefinitionParserDelegate.ABSTRACT_ATTRIBUTE.equals(name)) {
+ builder.setAbstract(Boolean.parseBoolean(attribute.getValue()));
+ } else if (BeanDefinitionParserDelegate.PARENT_ATTRIBUTE.equals(name)) {
+ builder.setParentName(attribute.getValue());
+ } else {
+ callback.process(element, attribute, builder, parserContext);
+ }
+ }
+ }
+
+ /**
+ *
+ * @author xi.hux@alipay.com
+ * @since 2.6.0
+ */
+ public interface AttributeCallback {
+
+ /**
+ * Parser attribute
+ *
+ * @param parent element parent
+ * @param attribute attribute
+ * @param builder builder
+ * @param parserContext parser context
+ */
+ void process(Element parent, Attr attribute, BeanDefinitionBuilder builder,
+ ParserContext parserContext);
+ }
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ApplicationShutdownCallbackPostProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ApplicationShutdownCallbackPostProcessor.java
index 6f33467b4..92475728d 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ApplicationShutdownCallbackPostProcessor.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ApplicationShutdownCallbackPostProcessor.java
@@ -19,14 +19,17 @@
import com.alipay.sofa.runtime.api.event.ApplicationShutdownCallback;
import com.alipay.sofa.runtime.spi.component.SofaRuntimeManager;
import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.core.PriorityOrdered;
/**
* @author xuanbei
* @author qilong.zql
* @since 2.5.0
*/
-public class ApplicationShutdownCallbackPostProcessor implements BeanPostProcessor {
+public class ApplicationShutdownCallbackPostProcessor implements MergedBeanDefinitionPostProcessor,
+ PriorityOrdered {
private SofaRuntimeManager sofaRuntimeManager;
@@ -34,6 +37,12 @@ public ApplicationShutdownCallbackPostProcessor(SofaRuntimeManager sofaRuntimeMa
this.sofaRuntimeManager = sofaRuntimeManager;
}
+ @Override
+ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
+ Class> beanType, String beanName) {
+ // no process
+ }
+
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
@@ -48,4 +57,9 @@ public Object postProcessAfterInitialization(Object bean, String beanName)
}
return bean;
}
+
+ @Override
+ public int getOrder() {
+ return PriorityOrdered.LOWEST_PRECEDENCE;
+ }
}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/AsyncProxyBeanPostProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/AsyncProxyBeanPostProcessor.java
new file mode 100644
index 000000000..68f25bc2a
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/AsyncProxyBeanPostProcessor.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spring;
+
+import com.alipay.sofa.infra.constants.SofaBootInfraConstants;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import com.alipay.sofa.runtime.spring.async.AsyncInitBeanHolder;
+import com.alipay.sofa.runtime.spring.async.AsyncTaskExecutor;
+import com.alipay.sofa.runtime.spring.parser.AsyncInitBeanDefinitionDecorator;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.support.AbstractApplicationContext;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * @author qilong.zql
+ * @author xuanbei
+ * @since 2.6.0
+ */
+public class AsyncProxyBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware,
+ InitializingBean {
+
+ private ApplicationContext applicationContext;
+
+ private String moduleName;
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ String methodName = AsyncInitBeanHolder.getAsyncInitMethodName(moduleName, beanName);
+ if (methodName == null || methodName.length() == 0) {
+ return bean;
+ }
+
+ ProxyFactory proxyFactory = new ProxyFactory();
+ proxyFactory.setTargetClass(bean.getClass());
+ proxyFactory.setProxyTargetClass(true);
+ AsyncInitializeBeanMethodInvoker asyncInitializeBeanMethodInvoker = new AsyncInitializeBeanMethodInvoker(
+ bean, beanName, methodName);
+ proxyFactory.addAdvice(asyncInitializeBeanMethodInvoker);
+ return proxyFactory.getProxy();
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ ConfigurableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext)
+ .getBeanFactory();
+ if (AsyncInitBeanDefinitionDecorator.isBeanLoadCostBeanFactory(beanFactory.getClass())) {
+ moduleName = AsyncInitBeanDefinitionDecorator.getModuleNameFromBeanFactory(beanFactory);
+ } else {
+ moduleName = SofaBootInfraConstants.ROOT_APPLICATION_CONTEXT;
+ }
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ class AsyncInitializeBeanMethodInvoker implements MethodInterceptor {
+ private final Object targetObject;
+ private final String asyncMethodName;
+ private final String beanName;
+ private final CountDownLatch initCountDownLatch = new CountDownLatch(1);
+ // mark async-init method is during first invocation.
+ private volatile boolean isAsyncCalling = false;
+ // mark init-method is called.
+ private volatile boolean isAsyncCalled = false;
+
+ AsyncInitializeBeanMethodInvoker(Object targetObject, String beanName, String methodName) {
+ this.targetObject = targetObject;
+ this.beanName = beanName;
+ this.asyncMethodName = methodName;
+ }
+
+ @Override
+ public Object invoke(final MethodInvocation invocation) throws Throwable {
+ // if the spring refreshing is finished
+ if (AsyncTaskExecutor.isStarted()) {
+ return invocation.getMethod().invoke(targetObject, invocation.getArguments());
+ }
+
+ Method method = invocation.getMethod();
+ final String methodName = method.getName();
+ if (!isAsyncCalled && methodName.equals(asyncMethodName)) {
+ isAsyncCalled = true;
+ isAsyncCalling = true;
+ AsyncTaskExecutor.submitTask(applicationContext.getEnvironment(), new Runnable() {
+ @Override
+ public void run() {
+ try {
+ long startTime = System.currentTimeMillis();
+ invocation.getMethod().invoke(targetObject, invocation.getArguments());
+ SofaLogger.info(String.format(
+ "%s(%s) %s method execute %dms, moduleName: %s.", targetObject
+ .getClass().getName(), beanName, methodName, (System
+ .currentTimeMillis() - startTime), moduleName));
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ } finally {
+ initCountDownLatch.countDown();
+ isAsyncCalling = false;
+ }
+ }
+ });
+ return null;
+ }
+
+ if (isAsyncCalling) {
+ long startTime = System.currentTimeMillis();
+ initCountDownLatch.await();
+ SofaLogger.info(String.format("%s(%s) %s method wait %dms, moduleName: %s.",
+ targetObject.getClass().getName(), beanName, methodName,
+ (System.currentTimeMillis() - startTime), moduleName));
+ }
+ return invocation.getMethod().invoke(targetObject, invocation.getArguments());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ClientFactoryBeanPostProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ClientFactoryBeanPostProcessor.java
index 232d7e877..d5a4571b9 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ClientFactoryBeanPostProcessor.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ClientFactoryBeanPostProcessor.java
@@ -88,6 +88,6 @@ public Object postProcessAfterInitialization(Object bean, String beanName)
@Override
public int getOrder() {
- return LOWEST_PRECEDENCE;
+ return PriorityOrdered.HIGHEST_PRECEDENCE;
}
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ExtensionClientBeanPostProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ExtensionClientBeanPostProcessor.java
new file mode 100644
index 000000000..9a736d1c6
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ExtensionClientBeanPostProcessor.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spring;
+
+import com.alipay.sofa.runtime.api.aware.ExtensionClientAware;
+import com.alipay.sofa.runtime.api.client.ExtensionClient;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.PriorityOrdered;
+
+/**
+ * {@link ExtensionClientAware}
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ExtensionClientBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
+
+ private ExtensionClient extensionClient;
+
+ public ExtensionClientBeanPostProcessor(ExtensionClient extensionClient) {
+ this.extensionClient = extensionClient;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(final Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof ExtensionClientAware) {
+ ((ExtensionClientAware) bean).setExtensionClient(extensionClient);
+ }
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public int getOrder() {
+ return PriorityOrdered.HIGHEST_PRECEDENCE;
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
index 5d6eae020..9e4155565 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/ReferenceAnnotationBeanPostProcessor.java
@@ -23,7 +23,7 @@
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
-import org.springframework.core.Ordered;
+import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -51,7 +51,7 @@
*
* @author xuanbei 18/5/9
*/
-public class ReferenceAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered {
+public class ReferenceAnnotationBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
private final PlaceHolderBinder binder = new DefaultPlaceHolderBinder();
private ApplicationContext applicationContext;
private SofaRuntimeContext sofaRuntimeContext;
@@ -184,7 +184,7 @@ private Object createReferenceProxy(SofaReference sofaReferenceAnnotation,
@Override
public int getOrder() {
- return Ordered.LOWEST_PRECEDENCE;
+ return PriorityOrdered.HIGHEST_PRECEDENCE;
}
class DefaultPlaceHolderBinder implements PlaceHolderBinder {
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/SofaRuntimeContextAwareProcessor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/SofaRuntimeContextAwareProcessor.java
index e049b8220..6cce8d8c7 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/SofaRuntimeContextAwareProcessor.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/SofaRuntimeContextAwareProcessor.java
@@ -20,13 +20,14 @@
import com.alipay.sofa.runtime.spi.spring.SofaRuntimeContextAware;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.PriorityOrdered;
/**
* @author qilong.zql
* @author khotyn
* @since 2.5.0
*/
-public class SofaRuntimeContextAwareProcessor implements BeanPostProcessor {
+public class SofaRuntimeContextAwareProcessor implements BeanPostProcessor, PriorityOrdered {
private SofaRuntimeContext sofaRuntimeContext;
public SofaRuntimeContextAwareProcessor(SofaRuntimeContext sofaRuntimeContext) {
@@ -48,4 +49,9 @@ public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
+
+ @Override
+ public int getOrder() {
+ return PriorityOrdered.HIGHEST_PRECEDENCE;
+ }
}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncInitBeanHolder.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncInitBeanHolder.java
new file mode 100644
index 000000000..73557bed2
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncInitBeanHolder.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spring.async;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author qilong.zql
+ * @author xuanbei
+ * @since 2.6.0
+ */
+public class AsyncInitBeanHolder {
+ private static final ConcurrentMap> asyncBeanInfos = new ConcurrentHashMap>();
+
+ public static void registerAsyncInitBean(String moduleName, String beanId, String methodName) {
+ if (moduleName == null || beanId == null || methodName == null) {
+ return;
+ }
+
+ Map asyncBeanInfosInModule = asyncBeanInfos.get(moduleName);
+ if (asyncBeanInfosInModule == null) {
+ asyncBeanInfos.putIfAbsent(moduleName, new ConcurrentHashMap());
+ asyncBeanInfosInModule = asyncBeanInfos.get(moduleName);
+ }
+
+ asyncBeanInfosInModule.put(beanId, methodName);
+ }
+
+ public static String getAsyncInitMethodName(String moduleName, String beanId) {
+ Map asyncBeanInfosInModule;
+ asyncBeanInfosInModule = (moduleName == null) ? null : asyncBeanInfos.get(moduleName);
+ return (beanId == null || asyncBeanInfosInModule == null) ? null : asyncBeanInfosInModule
+ .get(beanId);
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncTaskExecutionListener.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncTaskExecutionListener.java
new file mode 100644
index 000000000..d1ca1c36f
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncTaskExecutionListener.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spring.async;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.Ordered;
+import org.springframework.core.PriorityOrdered;
+
+/**
+ * @author qilong.zql
+ * @since 2.6.0
+ */
+public class AsyncTaskExecutionListener implements PriorityOrdered,
+ ApplicationListener,
+ ApplicationContextAware {
+ private ApplicationContext applicationContext;
+
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent event) {
+ if (applicationContext.equals(event.getApplicationContext())) {
+ AsyncTaskExecutor.ensureAsyncTasksFinish();
+ }
+ }
+
+ @Override
+ public int getOrder() {
+ // invoked after {@literal com.alipay.sofa.isle.spring.listener.SofaModuleContextRefreshedListener}
+ return Ordered.HIGHEST_PRECEDENCE + 1;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncTaskExecutor.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncTaskExecutor.java
new file mode 100644
index 000000000..e41d4d5ff
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/async/AsyncTaskExecutor.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spring.async;
+
+import com.alipay.sofa.infra.constants.SofaBootInfraConstants;
+import com.alipay.sofa.runtime.spi.log.SofaLogger;
+import com.alipay.sofa.runtime.util.NamedThreadFactory;
+import org.springframework.core.env.Environment;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author qilong.zql
+ * @author xuanbei
+ * @since 2.6.0
+ */
+public class AsyncTaskExecutor {
+ protected static final int CPU_COUNT = Runtime
+ .getRuntime()
+ .availableProcessors();
+ protected static final AtomicReference THREAD_POOL_REF = new AtomicReference();
+
+ protected static final List FUTURES = new ArrayList<>();
+ protected static final AtomicBoolean STARTED = new AtomicBoolean(
+ false);
+
+ public static Future submitTask(Environment environment, Runnable runnable) {
+ if (THREAD_POOL_REF.get() == null) {
+ ThreadPoolExecutor threadPoolExecutor = createThreadPoolExecutor(environment);
+ boolean success = THREAD_POOL_REF.compareAndSet(null, threadPoolExecutor);
+ if (!success) {
+ threadPoolExecutor.shutdown();
+ }
+ }
+ Future future = THREAD_POOL_REF.get().submit(runnable);
+ FUTURES.add(future);
+ return future;
+ }
+
+ /**
+ * Create thread pool to execute async init task
+ * @return
+ */
+ private static ThreadPoolExecutor createThreadPoolExecutor(Environment environment) {
+ int threadPoolCoreSize = CPU_COUNT + 1;
+ String coreSizeStr = environment
+ .getProperty(SofaBootInfraConstants.ASYNC_INIT_BEAN_CORE_SIZE);
+ if (coreSizeStr != null) {
+ threadPoolCoreSize = Integer.parseInt(coreSizeStr);
+ }
+
+ int threadPoolMaxSize = CPU_COUNT + 1;
+ String maxSizeStr = environment
+ .getProperty(SofaBootInfraConstants.ASYNC_INIT_BEAN_MAX_SIZE);
+ if (maxSizeStr != null) {
+ threadPoolMaxSize = Integer.parseInt(maxSizeStr);
+ }
+
+ SofaLogger.info(String.format(
+ "create async-init-bean thread pool, corePoolSize: %d, maxPoolSize: %d.",
+ threadPoolCoreSize, threadPoolMaxSize));
+ return new ThreadPoolExecutor(threadPoolCoreSize, threadPoolMaxSize, 30, TimeUnit.SECONDS,
+ new SynchronousQueue(), new NamedThreadFactory("async-init-bean"),
+ new ThreadPoolExecutor.CallerRunsPolicy());
+ }
+
+ public static void ensureAsyncTasksFinish() {
+ for (Future future : FUTURES) {
+ try {
+ future.get();
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ STARTED.set(true);
+ FUTURES.clear();
+ if (THREAD_POOL_REF.get() != null) {
+ THREAD_POOL_REF.get().shutdown();
+ THREAD_POOL_REF.set(null);
+ }
+ }
+
+ public static boolean isStarted() {
+ return STARTED.get();
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/config/SofaRuntimeConfigurationProperties.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/config/SofaRuntimeConfigurationProperties.java
index 32c226d62..af81275dc 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/config/SofaRuntimeConfigurationProperties.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/config/SofaRuntimeConfigurationProperties.java
@@ -16,15 +16,15 @@
*/
package com.alipay.sofa.runtime.spring.config;
+import com.alipay.sofa.infra.constants.SofaBootInfraConstants;
import com.alipay.sofa.runtime.SofaRuntimeProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author xuanbei 18/5/9
*/
-@ConfigurationProperties(SofaRuntimeConfigurationProperties.PREFIX)
+@ConfigurationProperties(SofaBootInfraConstants.PREFIX)
public class SofaRuntimeConfigurationProperties {
- static final String PREFIX = "com.alipay.sofa.boot";
public void setSkipJvmReferenceHealthCheck(boolean skipJvmReferenceHealthCheck) {
SofaRuntimeProperties.setSkipJvmReferenceHealthCheck(this.getClass().getClassLoader(),
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java
index b92309139..9006b4ff0 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/configuration/SofaRuntimeAutoConfiguration.java
@@ -16,6 +16,8 @@
*/
package com.alipay.sofa.runtime.spring.configuration;
+import com.alipay.sofa.runtime.spring.AsyncProxyBeanPostProcessor;
+import com.alipay.sofa.runtime.spring.async.AsyncTaskExecutionListener;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -45,6 +47,16 @@ public CloseApplicationContextCallBack closeApplicationContextCallBack() {
return new CloseApplicationContextCallBack();
}
+ @Bean
+ public AsyncProxyBeanPostProcessor asyncProxyBeanPostProcessor() {
+ return new AsyncProxyBeanPostProcessor();
+ }
+
+ @Bean
+ public AsyncTaskExecutionListener asyncTaskExecutionListener() {
+ return new AsyncTaskExecutionListener();
+ }
+
@Configuration
@ConditionalOnClass({ HealthChecker.class })
@AutoConfigureAfter(SofaRuntimeAutoConfiguration.class)
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java
index 10429d88a..551f4d142 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/AbstractContractFactoryBean.java
@@ -22,15 +22,6 @@
import javax.xml.parsers.DocumentBuilderFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.FactoryBean;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.config.TypedStringValue;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.w3c.dom.Element;
-import org.xml.sax.InputSource;
-
import com.alipay.sofa.runtime.api.ServiceRuntimeException;
import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants;
import com.alipay.sofa.runtime.service.binding.JvmBinding;
@@ -40,6 +31,14 @@
import com.alipay.sofa.runtime.spi.service.BindingConverter;
import com.alipay.sofa.runtime.spi.service.BindingConverterContext;
import com.alipay.sofa.runtime.spi.service.BindingConverterFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
/**
* Abstract Contract Factory Bean
@@ -93,18 +92,26 @@ public void afterPropertiesSet() throws Exception {
}
sofaRuntimeContext = applicationContext.getBean(
SofaRuntimeFrameworkConstants.SOFA_RUNTIME_CONTEXT_BEAN_ID, SofaRuntimeContext.class);
- bindingConverterFactory = applicationContext.getBean(
- SofaRuntimeFrameworkConstants.BINDING_CONVERTER_FACTORY_BEAN_ID,
- BindingConverterFactory.class);
- bindingAdapterFactory = applicationContext.getBean(
- SofaRuntimeFrameworkConstants.BINDING_ADAPTER_FACTORY_BEAN_ID,
- BindingAdapterFactory.class);
+ bindingConverterFactory = getBindingConverterFactory();
+ bindingAdapterFactory = getBindingAdapterFactory();
if (!apiType) {
this.bindings = parseBindings(tempElements, applicationContext, isInBinding());
}
doAfterPropertiesSet();
}
+ protected BindingConverterFactory getBindingConverterFactory() {
+ return applicationContext.getBean(
+ SofaRuntimeFrameworkConstants.BINDING_CONVERTER_FACTORY_BEAN_ID,
+ BindingConverterFactory.class);
+ }
+
+ protected BindingAdapterFactory getBindingAdapterFactory() {
+ return applicationContext.getBean(
+ SofaRuntimeFrameworkConstants.BINDING_ADAPTER_FACTORY_BEAN_ID,
+ BindingAdapterFactory.class);
+ }
+
protected List parseBindings(List parseElements,
ApplicationContext appContext, boolean isInBinding) {
List result = new ArrayList<>();
@@ -116,11 +123,7 @@ protected List parseBindings(List parseElements,
.getBindingConverterByTagName(tagName);
if (bindingConverter == null) {
- if (!tagName.equals(SofaRuntimeFrameworkConstants.BINDING_PREFIX
- + JvmBinding.JVM_BINDING_TYPE.toString())) {
- throw new ServiceRuntimeException("Can't find BindingConverter of type "
- + tagName);
- }
+ dealWithbindingConverterNotExist(tagName);
continue;
}
@@ -130,6 +133,7 @@ protected List parseBindings(List parseElements,
bindingConverterContext.setAppName(sofaRuntimeContext.getAppName());
bindingConverterContext.setAppClassLoader(sofaRuntimeContext.getAppClassLoader());
bindingConverterContext.setRepeatReferLimit(repeatReferLimit);
+ bindingConverterContext.setBeanId(beanId);
setProperties(bindingConverterContext);
@@ -142,6 +146,13 @@ protected List parseBindings(List parseElements,
return result;
}
+ protected void dealWithbindingConverterNotExist(String tagName) {
+ if (!tagName.equals(SofaRuntimeFrameworkConstants.BINDING_PREFIX
+ + JvmBinding.JVM_BINDING_TYPE.toString())) {
+ throw new ServiceRuntimeException("Can't find BindingConverter of type " + tagName);
+ }
+ }
+
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ReferenceFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ReferenceFactoryBean.java
index 8c6649eec..5711f6cc4 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ReferenceFactoryBean.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ReferenceFactoryBean.java
@@ -30,11 +30,11 @@
* @author xuanbei 18/3/1
*/
public class ReferenceFactoryBean extends AbstractContractFactoryBean {
- private Object proxy;
+ protected Object proxy;
/** jvm first or not */
- private boolean jvmFirst = true;
+ protected boolean jvmFirst = true;
/** load balance **/
- private String loadBalance;
+ protected String loadBalance;
public ReferenceFactoryBean() {
}
@@ -63,10 +63,9 @@ protected void doAfterPropertiesSet() throws Exception {
@Override
protected void setProperties(BindingConverterContext bindingConverterContext) {
bindingConverterContext.setLoadBalance(loadBalance);
- bindingConverterContext.setBeanId(beanId);
}
- private Reference buildReference() {
+ protected Reference buildReference() {
return new ReferenceImpl(uniqueId, getInterfaceClass(), InterfaceMode.spring, jvmFirst);
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java
index 090e178c0..f04a29b0b 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/factory/ServiceFactoryBean.java
@@ -33,8 +33,8 @@
* @author xuanbei 18/3/1
*/
public class ServiceFactoryBean extends AbstractContractFactoryBean {
- private Object ref;
- private Service service;
+ protected Object ref;
+ protected Service service;
public ServiceFactoryBean() {
}
@@ -89,7 +89,7 @@ protected void setProperties(BindingConverterContext bindingConverterContext) {
bindingConverterContext.setBeanId(beanId);
}
- private Service buildService() {
+ protected Service buildService() {
return new ServiceImpl(uniqueId, getInterfaceClass(), InterfaceMode.spring, ref);
}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/initializer/SofaRuntimeSpringContextInitializer.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/initializer/SofaRuntimeSpringContextInitializer.java
index e54ee3efd..06cb82a9b 100644
--- a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/initializer/SofaRuntimeSpringContextInitializer.java
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/initializer/SofaRuntimeSpringContextInitializer.java
@@ -26,6 +26,7 @@
import com.alipay.sofa.runtime.client.impl.ClientFactoryImpl;
import com.alipay.sofa.runtime.component.impl.StandardSofaRuntimeManager;
import com.alipay.sofa.runtime.constants.SofaRuntimeFrameworkConstants;
+import com.alipay.sofa.runtime.ext.client.ExtensionClientImpl;
import com.alipay.sofa.runtime.service.client.ReferenceClientImpl;
import com.alipay.sofa.runtime.service.client.ServiceClientImpl;
import com.alipay.sofa.runtime.service.impl.BindingAdapterFactoryImpl;
@@ -80,19 +81,23 @@ public static void initApplicationContext(ConfigurableApplicationContext applica
beanFactory.registerSingleton(SofaRuntimeFrameworkConstants.SOFA_RUNTIME_CONTEXT_BEAN_ID,
sofaRuntimeContext);
- beanFactory.registerSingleton(
- ReferenceAnnotationBeanPostProcessor.class.getCanonicalName(),
- new ReferenceAnnotationBeanPostProcessor(applicationContext, sofaRuntimeContext,
- bindingAdapterFactory, bindingConverterFactory));
- beanFactory.registerSingleton(ClientFactoryBeanPostProcessor.class.getCanonicalName(),
- new ClientFactoryBeanPostProcessor(sofaRuntimeContext.getClientFactory()));
+ // work on all bean
+ beanFactory.addBeanPostProcessor(new SofaRuntimeContextAwareProcessor(sofaRuntimeContext));
+ beanFactory.addBeanPostProcessor(new ClientFactoryBeanPostProcessor(sofaRuntimeContext
+ .getClientFactory()));
+ beanFactory.addBeanPostProcessor(new ExtensionClientBeanPostProcessor(
+ new ExtensionClientImpl(sofaRuntimeContext)));
beanFactory
- .registerSingleton(
- ApplicationShutdownCallbackPostProcessor.class.getCanonicalName(),
- new ApplicationShutdownCallbackPostProcessor(sofaRuntimeContext
- .getSofaRuntimeManager()));
- beanFactory.registerSingleton(SofaRuntimeContextAwareProcessor.class.getCanonicalName(),
- new SofaRuntimeContextAwareProcessor(sofaRuntimeContext));
+ .addBeanPostProcessor(new ReferenceAnnotationBeanPostProcessor(applicationContext,
+ sofaRuntimeContext, bindingAdapterFactory, bindingConverterFactory));
+
+ // work on all bean on the beginning, then reorder after all bean post processors being registered
+ ApplicationShutdownCallbackPostProcessor applicationShutdownCallbackPostProcessor = new ApplicationShutdownCallbackPostProcessor(
+ sofaRuntimeContext.getSofaRuntimeManager());
+ beanFactory.addBeanPostProcessor(applicationShutdownCallbackPostProcessor);
+ beanFactory.registerSingleton(
+ ApplicationShutdownCallbackPostProcessor.class.getCanonicalName(),
+ applicationShutdownCallbackPostProcessor);
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory)
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/AsyncInitBeanDefinitionDecorator.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/AsyncInitBeanDefinitionDecorator.java
new file mode 100644
index 000000000..324cd9b98
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/spring/parser/AsyncInitBeanDefinitionDecorator.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.spring.parser;
+
+import com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport;
+import com.alipay.sofa.infra.constants.SofaBootInfraConstants;
+import com.alipay.sofa.runtime.spring.async.AsyncInitBeanHolder;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Node;
+
+/**
+ * @author qilong.zql
+ * @author xuanbei
+ * @since 2.6.0
+ */
+public class AsyncInitBeanDefinitionDecorator implements BeanDefinitionDecorator,
+ SofaBootTagNameSupport {
+
+ private static final String BEAN_LOAD_COST_FACTORY_CLASS = "com.alipay.sofa.isle.spring.factory.BeanLoadCostBeanFactory";
+ private static final String GET_MODULE_NAME_METHOD = "getModuleName";
+
+ @Override
+ public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition,
+ ParserContext parserContext) {
+ if (!Boolean.TRUE.toString().equalsIgnoreCase(((Attr) node).getValue())) {
+ return definition;
+ }
+
+ String moduleName = getModuleName(parserContext);
+ if (moduleName != null && moduleName.trim().length() > 0) {
+ AsyncInitBeanHolder.registerAsyncInitBean(moduleName, definition.getBeanName(),
+ ((AbstractBeanDefinition) definition.getBeanDefinition()).getInitMethodName());
+ }
+ return definition;
+ }
+
+ @Override
+ public String supportTagName() {
+ return "async-init";
+ }
+
+ private String getModuleName(ParserContext parserContext) {
+ BeanDefinitionRegistry registry = parserContext.getRegistry();
+ if (registry instanceof AbstractApplicationContext) {
+ BeanFactory beanFactory = ((AbstractApplicationContext) registry).getBeanFactory();
+ if (isBeanLoadCostBeanFactory(beanFactory.getClass())) {
+ return getModuleNameFromBeanFactory(beanFactory);
+ }
+ }
+
+ if (isBeanLoadCostBeanFactory(registry.getClass())) {
+ return getModuleNameFromBeanFactory(registry);
+ }
+
+ return SofaBootInfraConstants.ROOT_APPLICATION_CONTEXT;
+ }
+
+ public static boolean isBeanLoadCostBeanFactory(Class factoryClass) {
+ if (factoryClass == null) {
+ return false;
+ }
+ if (BEAN_LOAD_COST_FACTORY_CLASS.equals(factoryClass.getName())) {
+ return true;
+ }
+
+ if (!Object.class.equals(factoryClass)) {
+ return isBeanLoadCostBeanFactory(factoryClass.getSuperclass());
+ }
+
+ return false;
+ }
+
+ public static String getModuleNameFromBeanFactory(Object factory) {
+ try {
+ return (String) factory.getClass().getMethod(GET_MODULE_NAME_METHOD).invoke(factory);
+ } catch (Throwable e) {
+ return SofaBootInfraConstants.ROOT_APPLICATION_CONTEXT;
+ }
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/util/NamedThreadFactory.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/util/NamedThreadFactory.java
new file mode 100644
index 000000000..31134b05e
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/runtime/util/NamedThreadFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.util;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author qilong.zql
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class NamedThreadFactory implements ThreadFactory {
+
+ static final AtomicInteger poolNumber = new AtomicInteger(1);
+ final AtomicInteger threadNumber = new AtomicInteger(1);
+ final ThreadGroup group;
+ final String namePrefix;
+ final boolean isDaemon;
+
+ public NamedThreadFactory(String name) {
+ this(name, false);
+ }
+
+ public NamedThreadFactory(String prefix, boolean daemon) {
+ SecurityManager s = System.getSecurityManager();
+ group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+ namePrefix = prefix + "-" + poolNumber.getAndIncrement() + "-thread-";
+ isDaemon = daemon;
+ }
+
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+ t.setDaemon(isDaemon);
+ if (t.getPriority() != Thread.NORM_PRIORITY) {
+ t.setPriority(Thread.NORM_PRIORITY);
+ }
+ return t;
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/Extensible.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/Extensible.java
new file mode 100644
index 000000000..94f25f34e
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/Extensible.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.service.api.component;
+
+/**
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+
+public interface Extensible {
+ /**
+ * register extension
+ *
+ * @param extension need to be registered
+ */
+ void registerExtension(Extension extension) throws Exception;
+
+ /**
+ * un-register extension
+ *
+ * @param extension need to be unregistered
+ */
+ void unregisterExtension(Extension extension) throws Exception;
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/Extension.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/Extension.java
new file mode 100644
index 000000000..591bd724b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/Extension.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.service.api.component;
+
+import com.alipay.sofa.runtime.api.component.ComponentName;
+import org.w3c.dom.Element;
+
+/**
+ * SOFA Extension Object
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public interface Extension {
+
+ /**
+ * Get component name for the extension
+ *
+ * @return Component name for the extension
+ */
+ ComponentName getComponentName();
+
+ /**
+ * Get Target Component Name。
+ *
+ * @return Target Component Name
+ */
+ ComponentName getTargetComponentName();
+
+ /**
+ * Get extension point Name
+ *
+ * @return Extension point Name
+ */
+ String getExtensionPoint();
+
+ /**
+ * Get extension element
+ *
+ * @return extension element
+ */
+ Element getElement();
+
+ /**
+ * Get contributions
+ *
+ * @return contributions
+ */
+ Object[] getContributions();
+
+ /**
+ * Get AppClassLoader
+ * @return appClassLoader
+ */
+ ClassLoader getAppClassLoader();
+
+ /**
+ * Identifies the extension inside the contributing component.
+ *
+ * @return id
+ */
+ String getId();
+
+}
diff --git a/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/ExtensionPoint.java b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/ExtensionPoint.java
new file mode 100644
index 000000000..0d0ae89ff
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/main/java/com/alipay/sofa/service/api/component/ExtensionPoint.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.service.api.component;
+
+import java.util.List;
+
+/**
+ * SOFA Extension Point Object
+ *
+ * @author xi.hux@alipay.com
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public interface ExtensionPoint {
+
+ /**
+ * Get extension point name
+ *
+ * @return extension point name
+ */
+ String getName();
+
+ /**
+ * Get all contributions class
+ *
+ * @return all contributions class
+ */
+ List> getContributions();
+
+ /**
+ * Get the comment
+ *
+ * @return the comment
+ */
+ String getDocumentation();
+
+ /**
+ * Whether has contribution or not
+ *
+ * @return true or false
+ */
+ boolean hasContribution();
+}
diff --git a/runtime-sofa-boot-starter/src/main/resources/META-INF/services/com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport b/runtime-sofa-boot-starter/src/main/resources/META-INF/services/com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport
index 0ef6d3383..fcb9bb2a4 100644
--- a/runtime-sofa-boot-starter/src/main/resources/META-INF/services/com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport
+++ b/runtime-sofa-boot-starter/src/main/resources/META-INF/services/com.alipay.sofa.infra.config.spring.namespace.spi.SofaBootTagNameSupport
@@ -36,3 +36,6 @@
com.alipay.sofa.runtime.spring.parser.ReferenceDefinitionParser
com.alipay.sofa.runtime.spring.parser.ServiceDefinitionParser
+com.alipay.sofa.runtime.spring.parser.AsyncInitBeanDefinitionDecorator
+com.alipay.sofa.runtime.ext.spring.parser.ExtensionPointBeanDefinitionParser
+com.alipay.sofa.runtime.ext.spring.parser.ExtensionBeanDefinitionParser
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/TimeWasteBean.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/TimeWasteBean.java
new file mode 100644
index 000000000..a23ed189c
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/TimeWasteBean.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.beans;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author qilong.zql
+ * @since 2.6.0
+ */
+public class TimeWasteBean {
+ private long printTime;
+ private static final AtomicInteger count = new AtomicInteger(0);
+
+ public void init() throws Exception {
+ hello();
+ TimeUnit.SECONDS.sleep(1);
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ hello();
+ }
+ });
+ t.start();
+ t.join();
+ count.getAndIncrement();
+ }
+
+ public String hello() {
+ printTime = System.currentTimeMillis();
+ return "world";
+ }
+
+ public long getPrintTime() {
+ return printTime;
+ }
+
+ public static int getCount() {
+ return count.get();
+ }
+
+ public static void resetCount() {
+ count.set(0);
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/ComponentLifeCycleServiceImpl.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/ComponentLifeCycleServiceImpl.java
new file mode 100644
index 000000000..5e3220b64
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/impl/ComponentLifeCycleServiceImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.beans.impl;
+
+import com.alipay.sofa.runtime.api.ServiceRuntimeException;
+import com.alipay.sofa.runtime.api.component.ComponentLifeCycle;
+import com.alipay.sofa.runtime.beans.service.LifeCycleService;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class ComponentLifeCycleServiceImpl implements LifeCycleService, ComponentLifeCycle {
+
+ private boolean activated;
+ private boolean deactivated;
+
+ @Override
+ public void activate() throws ServiceRuntimeException {
+ activated = true;
+ }
+
+ @Override
+ public void deactivate() throws ServiceRuntimeException {
+ deactivated = true;
+ }
+
+ @Override
+ public boolean isActivated() {
+ return activated;
+ }
+
+ @Override
+ public boolean isDeactivated() {
+ return deactivated;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/service/LifeCycleService.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/service/LifeCycleService.java
new file mode 100644
index 000000000..a4a001219
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/beans/service/LifeCycleService.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.beans.service;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public interface LifeCycleService {
+
+ boolean isActivated();
+
+ boolean isDeactivated();
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/AsyncInitTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/AsyncInitTest.java
new file mode 100644
index 000000000..141950864
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/AsyncInitTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration;
+
+import com.alipay.sofa.runtime.beans.TimeWasteBean;
+import com.alipay.sofa.runtime.integration.base.TestBase;
+import com.alipay.sofa.runtime.spring.async.AsyncTaskExecutor;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.ImportResource;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author qilong.zql
+ * @since 2.6.0
+ */
+public class AsyncInitTest extends TestBase {
+
+ @Before
+ public void before() {
+ try {
+ Field field = AsyncTaskExecutor.class.getDeclaredField("STARTED");
+ field.setAccessible(true);
+ AtomicBoolean atomicBoolean = (AtomicBoolean) field.get(null);
+ atomicBoolean.set(false);
+ } catch (Throwable throwable) {
+ // ignore;
+ }
+ }
+
+ @Test
+ public void testAsyncInitBean() {
+ long min = Long.MAX_VALUE;
+ long max = Long.MIN_VALUE;
+ initApplicationContext(new HashMap(), EmptyConfiguration.class);
+ Assert.assertEquals(12, TimeWasteBean.getCount());
+ for (int i = 1; i <= 10; i++) {
+ TimeWasteBean bean = applicationContext.getBean("testBean" + i, TimeWasteBean.class);
+ if (bean.getPrintTime() < min) {
+ min = bean.getPrintTime();
+ }
+ if (bean.getPrintTime() > max) {
+ max = bean.getPrintTime();
+ }
+ }
+ Assert.assertTrue("max:" + max + ", min:" + min, max - min < 3500);
+ TimeWasteBean.resetCount();
+ }
+
+ @EnableAutoConfiguration
+ @Configuration
+ @ImportResource({ "classpath*:META-INF/async/*.xml" })
+ static class EmptyConfiguration {
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java
index c2a96d424..9b3ed51fb 100644
--- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/IntegrationTest.java
@@ -129,7 +129,7 @@ public void testFactoryBean() {
.getBean("&"
+ SofaBeanNameGenerator.generateSofaReferenceBeanName(SampleService.class,
"methodBeanClassAnnotationSampleService"));
- Assert.assertTrue(serviceFactoryBean.isApiType());
+ Assert.assertTrue(referenceFactoryBean.isApiType());
/**
* {@link com.alipay.sofa.runtime.integration.base.AbstractTestBase.IntegrationTestConfiguration.BeforeConfiguration}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java
index 9bcb27bb7..4fc6ceb79 100644
--- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/TestSofaServiceAndReferenceException.java
@@ -23,7 +23,11 @@
import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding;
+import com.alipay.sofa.runtime.api.annotation.SofaServiceBinding;
+import com.alipay.sofa.runtime.api.binding.BindingType;
+import com.alipay.sofa.runtime.spring.factory.ServiceFactoryBean;
import org.apache.commons.io.FileUtils;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.FatalBeanException;
@@ -43,6 +47,13 @@
*/
public class TestSofaServiceAndReferenceException extends TestBase {
+ @After
+ public void closeApplication() {
+ if (applicationContext != null && applicationContext.isActive()) {
+ applicationContext.close();
+ }
+ }
+
@Test
public void testSofaReferenceOnMethodParameter() {
Map properties = new HashMap<>();
@@ -60,8 +71,7 @@ public void testSofaReferenceOnMethodParameter() {
@Test
public void testMultiSofaServiceWithSameInterfaceAndUniqueId() throws IOException {
- File sofaLog = new File(System.getProperty("user.home") + File.separator + "logs"
- + File.separator + "sofa-runtime" + File.separator
+ File sofaLog = new File("./logs" + File.separator + "sofa-runtime" + File.separator
+ "common-error.log");
FileUtils.write(sofaLog, "", System.getProperty("file.encoding"));
Map properties = new HashMap<>();
@@ -89,6 +99,29 @@ public void testMultiSofaReferenceFactoryMethod() {
initApplicationContext(properties, EmptyConfiguration.class);
}
+ @Test
+ public void testSofaServiceWithMultipleBindings() {
+ Map properties = new HashMap<>();
+ properties.put("spring.application.name", "runtime-test");
+ initApplicationContext(properties, MultipleBindingsSofaServiceConfiguration.class);
+ ServiceFactoryBean bean = applicationContext.getBean(ServiceFactoryBean.class);
+ Assert.assertEquals(2, bean.getBindings().size());
+ Assert.assertEquals(new BindingType("jvm"), bean.getBindings().get(0).getBindingType());
+ Assert.assertEquals(new BindingType("jvm"), bean.getBindings().get(1).getBindingType());
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ static class MultipleBindingsSofaServiceConfiguration {
+
+ // since the sofa-boot does not have any binding converter implementation, we can use two jvm bindings for now.
+ @Bean
+ @SofaService(bindings = { @SofaServiceBinding, @SofaServiceBinding })
+ SampleService sampleService() {
+ return new SampleServiceImpl("test");
+ }
+ }
+
@EnableAutoConfiguration
@Configuration
static class EmptyConfiguration {
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java
index 8a494e24a..ceffbf76f 100644
--- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/AbstractTestBase.java
@@ -28,7 +28,6 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/SofaBootTestApplication.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/bootstrap/SofaBootTestApplication.java
similarity index 95%
rename from runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/SofaBootTestApplication.java
rename to runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/bootstrap/SofaBootTestApplication.java
index 0bdbb501e..225c03836 100644
--- a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/base/SofaBootTestApplication.java
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/bootstrap/SofaBootTestApplication.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alipay.sofa.runtime.integration.base;
+package com.alipay.sofa.runtime.integration.bootstrap;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ImportResource;
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/component/ComponentLifeCycleTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/component/ComponentLifeCycleTest.java
new file mode 100644
index 000000000..49b768b25
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/component/ComponentLifeCycleTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.component;
+
+import com.alipay.sofa.runtime.beans.service.LifeCycleService;
+import com.alipay.sofa.runtime.integration.bootstrap.SofaBootTestApplication;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = SofaBootTestApplication.class)
+public class ComponentLifeCycleTest {
+
+ @Autowired
+ private LifeCycleService lifeCycleService;
+
+ @Autowired
+ private SofaRuntimeContext sofaRuntimeContext;
+
+ @Test
+ public void testActivated() {
+ Assert.assertTrue(lifeCycleService.isActivated());
+ }
+
+ @Test
+ @DirtiesContext
+ public void testDeactivated() {
+ Assert.assertFalse(lifeCycleService.isDeactivated());
+ sofaRuntimeContext.getComponentManager().shutdown();
+ Assert.assertTrue(lifeCycleService.isDeactivated());
+ }
+
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/ExtensionTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/ExtensionTest.java
new file mode 100644
index 000000000..9a4762951
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/ExtensionTest.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension;
+
+import com.alipay.sofa.common.xmap.Context;
+import com.alipay.sofa.common.xmap.DOMSerializer;
+import com.alipay.sofa.common.xmap.Resource;
+import com.alipay.sofa.common.xmap.XMap;
+import com.alipay.sofa.runtime.api.aware.ExtensionClientAware;
+import com.alipay.sofa.runtime.api.client.ExtensionClient;
+import com.alipay.sofa.runtime.api.client.param.ExtensionParam;
+import com.alipay.sofa.runtime.api.client.param.ExtensionPointParam;
+import com.alipay.sofa.runtime.integration.bootstrap.SofaBootTestApplication;
+import com.alipay.sofa.runtime.integration.extension.bean.IExtension;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringBean;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringListBean;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringMapBean;
+import com.alipay.sofa.runtime.integration.extension.descriptor.ClientExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.SimpleExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.XMapTestDescriptor;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.w3c.dom.Document;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.OutputStream;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = SofaBootTestApplication.class)
+public class ExtensionTest implements ExtensionClientAware {
+
+ private ExtensionClient extensionClient;
+
+ @Autowired
+ private IExtension iExtension;
+
+ @Autowired
+ private SimpleSpringBean simpleSpringBean;
+
+ @Autowired
+ private SimpleSpringListBean simpleSpringListBean1;
+
+ @Autowired
+ private SimpleSpringListBean simpleSpringListBean2;
+
+ @Autowired
+ private SimpleSpringMapBean springMapBean1;
+
+ @Autowired
+ private SimpleSpringMapBean springMapBean2;
+
+ @Test
+ public void testXMap() throws Exception {
+ XMap xMap = new XMap();
+ Resource resource = new Resource(new Context(), "META-INF/extension/extension-xmap.xml");
+ xMap.register(XMapTestDescriptor.class, true);
+ Object object = xMap.load(resource.toURL());
+ Assert.assertNotNull(object);
+ Assert.assertTrue(object instanceof XMapTestDescriptor);
+ XMapTestDescriptor xMapTestDescriptor = (XMapTestDescriptor) object;
+ Assert.assertEquals("xmaptest", xMapTestDescriptor.getValue());
+ Assert.assertEquals(1, xMap.loadAll(resource.toURL()).length);
+ }
+
+ @Test
+ public void testXMapAsString() throws Exception {
+ XMap xMap = new XMap();
+ xMap.register(XMapTestDescriptor.class);
+ Document document = xMap.asXml(new XMapTestDescriptor(), null);
+ Assert.assertNotNull(document);
+ Assert.assertEquals("xmaptest", document.getDocumentElement().getTagName());
+
+ String value = DOMSerializer.toString(document);
+ Assert.assertNotNull(value);
+ Assert.assertTrue(value.contains("xmaptest"));
+
+ OutputStream out = new ByteArrayOutputStream();
+ DOMSerializer.write(document, out);
+ Assert.assertNotNull(out);
+ Assert.assertTrue(out.toString().contains("xmaptest"));
+
+ DOMSerializer.write(document.getDocumentElement(), out);
+ Assert.assertNotNull(out);
+ Assert.assertTrue(out.toString().contains("xmaptest"));
+ }
+
+ @Test
+ public void testExtensionClient() throws Exception {
+ ExtensionPointParam extensionPointParam = new ExtensionPointParam();
+ extensionPointParam.setName("clientValue");
+ extensionPointParam.setTargetName("iExtension");
+ extensionPointParam.setTarget(iExtension);
+ extensionPointParam.setContributionClass(ClientExtensionDescriptor.class);
+ extensionClient.publishExtensionPoint(extensionPointParam);
+
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ Document doc = dBuilder.parse(new File(Thread.currentThread().getContextClassLoader()
+ .getResource("META-INF/extension/extension.xml").toURI()));
+ ExtensionParam extensionParam = new ExtensionParam();
+ extensionParam.setTargetName("clientValue");
+ extensionParam.setTargetInstanceName("iExtension");
+ extensionParam.setElement(doc.getDocumentElement());
+ extensionClient.publishExtension(extensionParam);
+
+ Assert.assertEquals("SOFABoot Extension Client Test", iExtension.getClientValue());
+ }
+
+ @Test
+ public void testSimple() throws Exception {
+ Assert.assertNotNull(iExtension);
+ Assert.assertNotNull(iExtension.getSimpleExtensionDescriptor());
+ Assert.assertEquals("SOFABoot Extension Test", iExtension.getSimpleExtensionDescriptor()
+ .getStringValue());
+ Assert.assertEquals("value with path", iExtension.getSimpleExtensionDescriptor()
+ .getStringValueWithPath());
+ Assert.assertEquals(new Integer(10), iExtension.getSimpleExtensionDescriptor()
+ .getIntValue());
+ Assert.assertEquals(new Long(20), iExtension.getSimpleExtensionDescriptor().getLongValue());
+ Assert.assertEquals(new Float(1.1), iExtension.getSimpleExtensionDescriptor()
+ .getFloatValue());
+ Assert.assertEquals(new Double(2.2), iExtension.getSimpleExtensionDescriptor()
+ .getDoubleValue());
+ Assert.assertEquals(Boolean.TRUE, iExtension.getSimpleExtensionDescriptor()
+ .getBooleanValue());
+ Assert.assertTrue(iExtension.getSimpleExtensionDescriptor().getDateValue().toString()
+ .contains("2019"));
+ Assert.assertEquals("file", iExtension.getSimpleExtensionDescriptor().getFileValue()
+ .getName());
+ Assert.assertEquals(SimpleExtensionDescriptor.class.getName(), iExtension
+ .getSimpleExtensionDescriptor().getClassValue().getName());
+ Assert.assertEquals("http://test", iExtension.getSimpleExtensionDescriptor().getUrlValue()
+ .toString());
+ Assert.assertEquals("extension.xml", iExtension.getSimpleExtensionDescriptor()
+ .getResourceValue().toFile().getName());
+ }
+
+ @Test
+ public void testList() {
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getValues().size());
+ Assert.assertTrue(iExtension.getListExtensionDescriptor().getValues().contains("test1"));
+ Assert.assertTrue(iExtension.getListExtensionDescriptor().getValues().contains("test2"));
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getIntValues().length);
+ Assert.assertEquals(1, iExtension.getListExtensionDescriptor().getIntValues()[0]);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getIntValues()[1]);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getLongValues().length);
+ Assert.assertEquals(11, iExtension.getListExtensionDescriptor().getLongValues()[0]);
+ Assert.assertEquals(22, iExtension.getListExtensionDescriptor().getLongValues()[1]);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getFloatValues().length);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getDoubleValues().length);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getBooleanValues().length);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getCharValues().length);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getShortValues().length);
+ Assert.assertEquals(2, iExtension.getListExtensionDescriptor().getByteValues().length);
+ }
+
+ @Test
+ public void testMap() {
+ Assert.assertEquals(2, iExtension.getTestMap().size());
+ Assert.assertEquals("testMapValue1", iExtension.getTestMap().get("testMapKey1"));
+ Assert.assertEquals("testMapValue2", iExtension.getTestMap().get("testMapKey2"));
+ }
+
+ @Test
+ public void testSimpleString() {
+ Assert.assertNotNull(iExtension.getSimpleSpringBean());
+ Assert.assertEquals(iExtension.getSimpleSpringBean(), simpleSpringBean);
+ }
+
+ @Test
+ public void testSpringList() {
+ Assert.assertEquals(2, iExtension.getSimpleSpringListBeans().size());
+ Assert.assertTrue(iExtension.getSimpleSpringListBeans().contains(simpleSpringListBean1));
+ Assert.assertTrue(iExtension.getSimpleSpringListBeans().contains(simpleSpringListBean2));
+ }
+
+ @Test
+ public void testSpringMap() {
+ Assert.assertEquals(2, iExtension.getSimpleSpringMapBeanMap().size());
+ Assert.assertEquals(springMapBean1,
+ iExtension.getSimpleSpringMapBeanMap().get("testMapSpringKey1"));
+ Assert.assertEquals(springMapBean2,
+ iExtension.getSimpleSpringMapBeanMap().get("testMapSpringKey2"));
+ }
+
+ @Test
+ public void testContext() {
+ Assert.assertEquals("testContextValue\n", iExtension.getTestContextValue());
+ }
+
+ @Test
+ public void testParent() {
+ Assert.assertEquals("testParentValue", iExtension.getTestParentValue());
+ }
+
+ @Test
+ public void testBad() {
+ Assert.assertNull(iExtension.getBadDescriptor());
+ }
+
+ @Override
+ public void setExtensionClient(ExtensionClient extensionClient) {
+ this.extensionClient = extensionClient;
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/IExtension.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/IExtension.java
new file mode 100755
index 000000000..325246c2a
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/IExtension.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.bean;
+
+import com.alipay.sofa.runtime.integration.extension.descriptor.ListExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.SimpleExtensionDescriptor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public interface IExtension {
+
+ /**
+ * Extension Client Test
+ */
+ String getClientValue();
+
+ /**
+ * Simple XNode Test
+ */
+ SimpleExtensionDescriptor getSimpleExtensionDescriptor();
+
+ /**
+ * XNodeList Test
+ */
+ ListExtensionDescriptor getListExtensionDescriptor();
+
+ /**
+ * XNodeMap Test
+ */
+ Map getTestMap();
+
+ /**
+ * XNodeSpring Test
+ */
+ SimpleSpringBean getSimpleSpringBean();
+
+ /**
+ * XNodeSpringList Test
+ */
+ List getSimpleSpringListBeans();
+
+ /**
+ * XNodeSpringMap Test
+ */
+ Map getSimpleSpringMapBeanMap();
+
+ /**
+ * XContext Test
+ */
+ String getTestContextValue();
+
+ /**
+ * XParent Test
+ */
+ String getTestParentValue();
+
+ /**
+ * Bad Test
+ */
+ SimpleExtensionDescriptor getBadDescriptor();
+
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/IExtensionImpl.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/IExtensionImpl.java
new file mode 100755
index 000000000..73b90568b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/IExtensionImpl.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.bean;
+
+import com.alipay.sofa.service.api.component.Extension;
+import com.alipay.sofa.runtime.integration.extension.descriptor.ClientExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.ContextExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.MapExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.ParentExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.SimpleExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.ListExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.SpringListExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.SpringMapExtensionDescriptor;
+import com.alipay.sofa.runtime.integration.extension.descriptor.SpringSimpleExtensionDescriptor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class IExtensionImpl implements IExtension {
+
+ private String clientValue;
+
+ private SimpleExtensionDescriptor simpleExtensionDescriptor;
+
+ private ListExtensionDescriptor listExtensionDescriptor;
+
+ private Map testMap = new HashMap<>();
+
+ private SimpleSpringBean simpleSpringBean;
+
+ private List simpleSpringListBeans = new ArrayList<>();
+
+ private Map simpleSpringMapBeanMap = new HashMap<>();
+
+ private String testContextValue;
+
+ private String testParentValue;
+
+ private SimpleExtensionDescriptor badDescriptor;
+
+ @Override
+ public String getClientValue() {
+ return clientValue;
+ }
+
+ public SimpleExtensionDescriptor getSimpleExtensionDescriptor() {
+ return simpleExtensionDescriptor;
+ }
+
+ public ListExtensionDescriptor getListExtensionDescriptor() {
+ return listExtensionDescriptor;
+ }
+
+ @Override
+ public Map getTestMap() {
+ return testMap;
+ }
+
+ @Override
+ public SimpleSpringBean getSimpleSpringBean() {
+ return simpleSpringBean;
+ }
+
+ @Override
+ public List getSimpleSpringListBeans() {
+ return simpleSpringListBeans;
+ }
+
+ @Override
+ public Map getSimpleSpringMapBeanMap() {
+ return simpleSpringMapBeanMap;
+ }
+
+ public String getTestContextValue() {
+ return testContextValue;
+ }
+
+ public String getTestParentValue() {
+ return testParentValue;
+ }
+
+ public SimpleExtensionDescriptor getBadDescriptor() {
+ return badDescriptor;
+ }
+
+ /**
+ * Component method, framework will invoke this method to contribute the extension to the existing extension point.
+ *
+ * @param extension extension
+ * @throws Exception any exception
+ */
+ public void registerExtension(Extension extension) throws Exception {
+ Object[] contributions = extension.getContributions();
+ String extensionPoint = extension.getExtensionPoint();
+
+ if (contributions == null) {
+ return;
+ }
+
+ for (Object contribution : contributions) {
+ if ("clientValue".equals(extensionPoint)) {
+ clientValue = ((ClientExtensionDescriptor) contribution).getValue();
+ } else if ("simple".equals(extensionPoint)) {
+ simpleExtensionDescriptor = (SimpleExtensionDescriptor) contribution;
+ } else if ("testList".equals(extensionPoint)) {
+ listExtensionDescriptor = (ListExtensionDescriptor) contribution;
+ } else if ("testMap".equals(extensionPoint)) {
+ testMap.putAll(((MapExtensionDescriptor) contribution).getValues());
+ } else if ("simpleSpring".equals(extensionPoint)) {
+ simpleSpringBean = ((SpringSimpleExtensionDescriptor) contribution).getValue();
+ } else if ("testSpringList".equals(extensionPoint)) {
+ simpleSpringListBeans.addAll(((SpringListExtensionDescriptor) contribution)
+ .getValues());
+ } else if ("testSpringMap".equals(extensionPoint)) {
+ simpleSpringMapBeanMap.putAll(((SpringMapExtensionDescriptor) contribution)
+ .getValues());
+ } else if ("testContext".equals(extensionPoint)) {
+ testContextValue = ((ContextExtensionDescriptor) contribution).getContextValue();
+ } else if ("testParent".equals(extensionPoint)) {
+ testParentValue = ((ParentExtensionDescriptor) contribution)
+ .getSubExtensionDescriptor().getParentValue().getValue();
+ } else if ("bad".equals(extensionPoint)) {
+ badDescriptor = (SimpleExtensionDescriptor) contribution;
+ ;
+ }
+ }
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringBean.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringBean.java
new file mode 100644
index 000000000..1e43cd434
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringBean.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.bean;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class SimpleSpringBean {
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringListBean.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringListBean.java
new file mode 100644
index 000000000..9bee02b83
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringListBean.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.bean;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class SimpleSpringListBean {
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringMapBean.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringMapBean.java
new file mode 100644
index 000000000..6accbd80b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringMapBean.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.bean;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class SimpleSpringMapBean {
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringMapBeanWithXObject.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringMapBeanWithXObject.java
new file mode 100644
index 000000000..e3fcb85d0
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/bean/SimpleSpringMapBeanWithXObject.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.bean;
+
+import com.alipay.sofa.common.xmap.annotation.XObject;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("springMap")
+public class SimpleSpringMapBeanWithXObject {
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ClientExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ClientExtensionDescriptor.java
new file mode 100755
index 000000000..338dfafb1
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ClientExtensionDescriptor.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XNode;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("clientValue")
+public class ClientExtensionDescriptor {
+ @XNode("value")
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ContextExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ContextExtensionDescriptor.java
new file mode 100755
index 000000000..b05e507ef
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ContextExtensionDescriptor.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XContent;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("testContext")
+public class ContextExtensionDescriptor {
+
+ @XContent(value = "value")
+ private String contextValue;
+
+ public String getContextValue() {
+ return contextValue;
+ }
+
+ public void setContextValue(String contextValue) {
+ this.contextValue = contextValue;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ListExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ListExtensionDescriptor.java
new file mode 100755
index 000000000..7ee818f26
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ListExtensionDescriptor.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XNodeList;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("testList")
+public class ListExtensionDescriptor {
+
+ @XNodeList(value = "value", componentType = String.class, type = ArrayList.class)
+ private List values;
+
+ @XNodeList(value = "attribute/value[@id='listTest']", componentType = String.class, type = String[].class)
+ private String[] attributeValues;
+
+ @XNodeList(value = "value", componentType = String.class, type = LinkedList.class)
+ private List LinkedListValues;
+
+ @XNodeList(value = "intValue", componentType = int.class, type = int[].class)
+ int[] intValues;
+
+ @XNodeList(value = "longValue", componentType = long.class, type = long[].class)
+ long[] longValues;
+
+ @XNodeList(value = "floatValue", componentType = float.class, type = float[].class)
+ float[] floatValues;
+
+ @XNodeList(value = "doubleValue", componentType = double.class, type = double[].class)
+ double[] doubleValues;
+
+ @XNodeList(value = "booleanValue", componentType = boolean.class, type = boolean[].class)
+ boolean[] booleanValues;
+
+ @XNodeList(value = "charValue", componentType = char.class, type = char[].class)
+ char[] charValues;
+
+ @XNodeList(value = "shortValue", componentType = short.class, type = short[].class)
+ short[] shortValues;
+
+ @XNodeList(value = "byteValue", componentType = byte.class, type = byte[].class)
+ byte[] byteValues;
+
+ public List getValues() {
+ return values;
+ }
+
+ public String[] getAttributeValues() {
+ return attributeValues;
+ }
+
+ public int[] getIntValues() {
+ return intValues;
+ }
+
+ public long[] getLongValues() {
+ return longValues;
+ }
+
+ public float[] getFloatValues() {
+ return floatValues;
+ }
+
+ public double[] getDoubleValues() {
+ return doubleValues;
+ }
+
+ public boolean[] getBooleanValues() {
+ return booleanValues;
+ }
+
+ public char[] getCharValues() {
+ return charValues;
+ }
+
+ public short[] getShortValues() {
+ return shortValues;
+ }
+
+ public byte[] getByteValues() {
+ return byteValues;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/MapExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/MapExtensionDescriptor.java
new file mode 100755
index 000000000..eff2d2a13
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/MapExtensionDescriptor.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XNodeMap;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringMapBean;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("testMap")
+public class MapExtensionDescriptor {
+
+ @XNodeMap(value = "value", key = "@name", componentType = String.class, type = HashMap.class)
+ private Map values;
+
+ @XNodeMap(value = "simple", key = "@key", componentType = SimpleExtensionDescriptor.class, type = HashMap.class)
+ private Map simpleValues;
+
+ @XNodeMap(value = "value/attribute[@id='mapTest']", key = "@name", componentType = SimpleSpringMapBean.class, type = HashMap.class)
+ private Map valueAttributes;
+
+ public Map getValues() {
+ return values;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ParentExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ParentExtensionDescriptor.java
new file mode 100755
index 000000000..fe7ca12fc
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/ParentExtensionDescriptor.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XNode;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject(value = "testParent", order = { "a", "b" })
+public class ParentExtensionDescriptor {
+
+ @XNode("value")
+ private String value;
+
+ @XNode("testSub")
+ private SubExtensionDescriptor subExtensionDescriptor;
+
+ public SubExtensionDescriptor getSubExtensionDescriptor() {
+ return subExtensionDescriptor;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void setSubExtensionDescriptor(SubExtensionDescriptor subExtensionDescriptor) {
+ this.subExtensionDescriptor = subExtensionDescriptor;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SimpleExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SimpleExtensionDescriptor.java
new file mode 100755
index 000000000..02a7c9992
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SimpleExtensionDescriptor.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.Resource;
+import com.alipay.sofa.common.xmap.annotation.XNode;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Date;
+
+/**
+ * @author khotyn
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("simple")
+public class SimpleExtensionDescriptor {
+
+ @XNode("value")
+ private String stringValue;
+
+ @XNode("path/value")
+ private String stringValueWithPath;
+
+ @XNode("intValue")
+ private Integer intValue;
+
+ @XNode("longValue")
+ private Long longValue;
+
+ @XNode("floatValue")
+ private Float floatValue;
+
+ @XNode("doubleValue")
+ private Double doubleValue;
+
+ @XNode("booleanValue")
+ private Boolean booleanValue;
+
+ @XNode("dateValue")
+ private Date dateValue;
+
+ @XNode("fileValue")
+ private File fileValue;
+
+ @XNode("urlValue")
+ private URL urlValue;
+
+ @XNode("classValue")
+ private Class classValue;
+
+ @XNode("resourceValue")
+ private Resource resourceValue;
+
+ public String getStringValue() {
+ return stringValue;
+ }
+
+ public void setStringValue(String stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ public String getStringValueWithPath() {
+ return stringValueWithPath;
+ }
+
+ public void setStringValueWithPath(String stringValueWithPath) {
+ this.stringValueWithPath = stringValueWithPath;
+ }
+
+ public Integer getIntValue() {
+ return intValue;
+ }
+
+ public void setIntValue(Integer intValue) {
+ this.intValue = intValue;
+ }
+
+ public Long getLongValue() {
+ return longValue;
+ }
+
+ public void setLongValue(Long longValue) {
+ this.longValue = longValue;
+ }
+
+ public Float getFloatValue() {
+ return floatValue;
+ }
+
+ public void setFloatValue(Float floatValue) {
+ this.floatValue = floatValue;
+ }
+
+ public Double getDoubleValue() {
+ return doubleValue;
+ }
+
+ public void setDoubleValue(Double doubleValue) {
+ this.doubleValue = doubleValue;
+ }
+
+ public Boolean getBooleanValue() {
+ return booleanValue;
+ }
+
+ public void setBooleanValue(Boolean booleanValue) {
+ this.booleanValue = booleanValue;
+ }
+
+ public Date getDateValue() {
+ return dateValue;
+ }
+
+ public void setDateValue(Date dateValue) {
+ this.dateValue = dateValue;
+ }
+
+ public File getFileValue() {
+ return fileValue;
+ }
+
+ public void setFileValue(File fileValue) {
+ this.fileValue = fileValue;
+ }
+
+ public URL getUrlValue() {
+ return urlValue;
+ }
+
+ public void setUrlValue(URL urlValue) {
+ this.urlValue = urlValue;
+ }
+
+ public Class getClassValue() {
+ return classValue;
+ }
+
+ public void setClassValue(Class classValue) {
+ this.classValue = classValue;
+ }
+
+ public Resource getResourceValue() {
+ return resourceValue;
+ }
+
+ public void setResourceValue(Resource resourceValue) {
+ this.resourceValue = resourceValue;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringListExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringListExtensionDescriptor.java
new file mode 100755
index 000000000..5da5e3390
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringListExtensionDescriptor.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import com.alipay.sofa.common.xmap.spring.XNodeListSpring;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringListBean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("testSpringList")
+public class SpringListExtensionDescriptor {
+
+ @XNodeListSpring(value = "value", componentType = SimpleSpringListBean.class, type = ArrayList.class)
+ private List values;
+
+ public List getValues() {
+ return values;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringMapExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringMapExtensionDescriptor.java
new file mode 100755
index 000000000..823c3458e
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringMapExtensionDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import com.alipay.sofa.common.xmap.spring.XNodeMapSpring;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringMapBean;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringMapBeanWithXObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("testSpringMap")
+public class SpringMapExtensionDescriptor {
+
+ @XNodeMapSpring(value = "value", key = "@name", componentType = SimpleSpringMapBean.class, type = HashMap.class)
+ private Map values;
+
+ @XNodeMapSpring(value = "value/attribute[@id='springMapTest']", key = "@name", componentType = SimpleSpringMapBeanWithXObject.class, type = HashMap.class)
+ private Map valueAttributes;
+
+ public Map getValues() {
+ return values;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringSimpleExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringSimpleExtensionDescriptor.java
new file mode 100755
index 000000000..35ef6de1e
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SpringSimpleExtensionDescriptor.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import com.alipay.sofa.common.xmap.spring.XNodeSpring;
+import com.alipay.sofa.runtime.integration.extension.bean.SimpleSpringBean;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("simpleSpring")
+public class SpringSimpleExtensionDescriptor {
+
+ @XNodeSpring(value = "value", type = SimpleSpringBean.class)
+ private SimpleSpringBean value;
+
+ /**
+ *
+ * @return
+ */
+ public SimpleSpringBean getValue() {
+ return value;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SubExtensionDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SubExtensionDescriptor.java
new file mode 100644
index 000000000..4ca83e8c0
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/SubExtensionDescriptor.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XObject;
+import com.alipay.sofa.common.xmap.annotation.XParent;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject("testSub")
+public class SubExtensionDescriptor {
+
+ @XParent
+ private ParentExtensionDescriptor parentValue;
+
+ public ParentExtensionDescriptor getParentValue() {
+ return parentValue;
+ }
+
+ @XParent
+ public void setParentValue(ParentExtensionDescriptor parentValue) {
+ this.parentValue = parentValue;
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/XMapTestDescriptor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/XMapTestDescriptor.java
new file mode 100755
index 000000000..8a29b7e57
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/extension/descriptor/XMapTestDescriptor.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.extension.descriptor;
+
+import com.alipay.sofa.common.xmap.annotation.XNode;
+import com.alipay.sofa.common.xmap.annotation.XNodeList;
+import com.alipay.sofa.common.xmap.annotation.XObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@XObject(value = "xmaptest", order = { "value1", "value2" })
+public class XMapTestDescriptor {
+ @XNode("value")
+ private String value;
+
+ @XNodeList(value = "values", trim = false, type = ArrayList.class, componentType = String.class)
+ private List values = new ArrayList<>();
+
+ public String getValue() {
+ return value;
+ }
+
+ public List getValues() {
+ return values;
+ }
+}
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/BeanPostProcessorOrderTest.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/BeanPostProcessorOrderTest.java
new file mode 100644
index 000000000..ef8224e37
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/BeanPostProcessorOrderTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.postprocessor;
+
+import com.alipay.sofa.runtime.api.event.ApplicationShutdownCallback;
+import com.alipay.sofa.runtime.integration.bootstrap.SofaBootTestApplication;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeManager;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = SofaBootTestApplication.class)
+public class BeanPostProcessorOrderTest {
+
+ @Autowired
+ private SofaRuntimeContext sofaRuntimeContext;
+
+ @Autowired
+ private HigherOrderBeanPostProcessor higherOrderBeanPostProcessor;
+
+ @Autowired
+ private BeanPostProcessorTestBean beanPostProcessorTestBean;
+
+ @Test
+ public void testClientFactoryInject() {
+ Assert.assertNotNull(higherOrderBeanPostProcessor.getClientFactory());
+ }
+
+ @Test
+ public void testSofaRuntimeContextInject() {
+ Assert.assertNotNull(higherOrderBeanPostProcessor.getSofaRuntimeContext());
+ }
+
+ @Test
+ public void testShutDownHook() throws Exception {
+ List applicationShutdownCallbacks = getApplicationShutdownCallbacks();
+ Assert.assertNotNull(applicationShutdownCallbacks);
+ Assert.assertTrue(applicationShutdownCallbacks.contains(higherOrderBeanPostProcessor));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testShutDownHookReordered() throws Exception {
+ List applicationShutdownCallbacks = getApplicationShutdownCallbacks();
+ BeanPostProcessorTestBean testBeanCallBack = null;
+ for (ApplicationShutdownCallback applicationShutdownCallback : applicationShutdownCallbacks) {
+ if (BeanPostProcessorTestBean.class.getName().equals(
+ applicationShutdownCallback.getClass().getName())) {
+ testBeanCallBack = (BeanPostProcessorTestBean) applicationShutdownCallback;
+ }
+ }
+ Assert.assertNotNull(testBeanCallBack);
+ Assert.assertEquals(testBeanCallBack, beanPostProcessorTestBean);
+ Assert.assertTrue(testBeanCallBack.isEnhancedByLowOrderPostProcessor());
+ }
+
+ @Test
+ public void testSofaReference() throws Exception {
+ Assert.assertNotNull(higherOrderBeanPostProcessor.getSampleService());
+ }
+
+ @SuppressWarnings("unchecked")
+ private List getApplicationShutdownCallbacks() throws Exception {
+ SofaRuntimeManager sofaRuntimeManager = sofaRuntimeContext.getSofaRuntimeManager();
+ Field applicationShutdownCallbacksField = sofaRuntimeManager.getClass().getDeclaredField(
+ "applicationShutdownCallbacks");
+ applicationShutdownCallbacksField.setAccessible(true);
+ return (List) applicationShutdownCallbacksField
+ .get(sofaRuntimeManager);
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/BeanPostProcessorTestBean.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/BeanPostProcessorTestBean.java
new file mode 100644
index 000000000..da443533b
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/BeanPostProcessorTestBean.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.postprocessor;
+
+import com.alipay.sofa.runtime.api.event.ApplicationShutdownCallback;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class BeanPostProcessorTestBean implements ApplicationShutdownCallback {
+
+ private boolean enhancedByLowOrderPostProcessor = false;
+
+ public BeanPostProcessorTestBean() {
+ }
+
+ public BeanPostProcessorTestBean(boolean enhancedByLowOrderPostProcessor) {
+ this.enhancedByLowOrderPostProcessor = enhancedByLowOrderPostProcessor;
+ }
+
+ public boolean isEnhancedByLowOrderPostProcessor() {
+ return enhancedByLowOrderPostProcessor;
+ }
+
+ @Override
+ public void shutdown() throws Throwable {
+
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/HigherOrderBeanPostProcessor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/HigherOrderBeanPostProcessor.java
new file mode 100644
index 000000000..3dd4a3f1c
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/HigherOrderBeanPostProcessor.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.postprocessor;
+
+import com.alipay.sofa.runtime.api.annotation.SofaReference;
+import com.alipay.sofa.runtime.api.aware.ClientFactoryAware;
+import com.alipay.sofa.runtime.api.client.ClientFactory;
+import com.alipay.sofa.runtime.api.event.ApplicationShutdownCallback;
+import com.alipay.sofa.runtime.beans.service.SampleService;
+import com.alipay.sofa.runtime.spi.component.SofaRuntimeContext;
+import com.alipay.sofa.runtime.spi.spring.SofaRuntimeContextAware;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.PriorityOrdered;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class HigherOrderBeanPostProcessor implements BeanPostProcessor, PriorityOrdered,
+ SofaRuntimeContextAware, ClientFactoryAware,
+ ApplicationShutdownCallback {
+
+ private SofaRuntimeContext sofaRuntimeContext;
+
+ private ClientFactory clientFactory;
+
+ @SofaReference
+ private SampleService sampleService;
+
+ @Override
+ public int getOrder() {
+ return PriorityOrdered.HIGHEST_PRECEDENCE;
+ }
+
+ @Override
+ public void setClientFactory(ClientFactory clientFactory) {
+ this.clientFactory = clientFactory;
+ }
+
+ @Override
+ public void setSofaRuntimeContext(SofaRuntimeContext sofaRuntimeContext) {
+ this.sofaRuntimeContext = sofaRuntimeContext;
+ }
+
+ public SofaRuntimeContext getSofaRuntimeContext() {
+ return sofaRuntimeContext;
+ }
+
+ public ClientFactory getClientFactory() {
+ return clientFactory;
+ }
+
+ public SampleService getSampleService() {
+ return sampleService;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public void shutdown() throws Throwable {
+ }
+
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/LowOrderBeanPostProcessor.java b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/LowOrderBeanPostProcessor.java
new file mode 100644
index 000000000..60c2af476
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/java/com/alipay/sofa/runtime/integration/postprocessor/LowOrderBeanPostProcessor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.alipay.sofa.runtime.integration.postprocessor;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+/**
+ *
+ * @author ruoshan
+ * @since 2.6.0
+ */
+public class LowOrderBeanPostProcessor implements BeanPostProcessor {
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof BeanPostProcessorTestBean) {
+ bean = new BeanPostProcessorTestBean(true);
+ }
+ return bean;
+ }
+}
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/resources/META-INF/async/async-test.xml b/runtime-sofa-boot-starter/src/test/resources/META-INF/async/async-test.xml
new file mode 100644
index 000000000..d96ec42e4
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/resources/META-INF/async/async-test.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/resources/META-INF/extension/extension-xmap.xml b/runtime-sofa-boot-starter/src/test/resources/META-INF/extension/extension-xmap.xml
new file mode 100755
index 000000000..bb3399830
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/resources/META-INF/extension/extension-xmap.xml
@@ -0,0 +1,5 @@
+
+ xmaptest
+ test1
+ test2
+
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/resources/META-INF/extension/extension.xml b/runtime-sofa-boot-starter/src/test/resources/META-INF/extension/extension.xml
new file mode 100755
index 000000000..bcb9560df
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/resources/META-INF/extension/extension.xml
@@ -0,0 +1,3 @@
+
+ SOFABoot Extension Client Test
+
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-extension.xml b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-extension.xml
new file mode 100644
index 000000000..1f1c26800
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-extension.xml
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SOFABoot Extension Test
+
+ value with path
+
+ 10
+ 20
+ 1.1
+ 2.2
+ true
+ 17-01-2019
+ file
+ http://test
+ com.alipay.sofa.runtime.integration.extension.descriptor.SimpleExtensionDescriptor
+ META-INF/extension/extension.xml
+
+
+
+
+
+
+
+
+
+
+
+
+ test1
+ test2
+
+ attributeTest1
+ attributeTest2
+
+ 1
+ 2
+ 11
+ 22
+ 1.1
+ 2.2
+ 11.11
+ 22.22
+ true
+ false
+ a
+ b
+ 33
+ 44
+ 7
+ 8
+
+
+
+
+
+
+
+
+
+
+
+
+ testMapValue1
+ testMapValue2
+
+
+ test
+
+
+
+ value1
+ value2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ simpleSpringBean
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ simpleSpringListBean1
+ simpleSpringListBean2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ springMapBean1
+ springMapBean2
+
+ value1
+ value2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ testContextValue
+
+
+
+
+
+
+
+
+
+
+
+
+ testParentValue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ bad
+
+
+
+
+
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-postprocessor-order.xml b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-postprocessor-order.xml
new file mode 100644
index 000000000..db5907844
--- /dev/null
+++ b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-postprocessor-order.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml
index c7a05f419..97088ebda 100644
--- a/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml
+++ b/runtime-sofa-boot-starter/src/test/resources/META-INF/spring/test-service.xml
@@ -31,4 +31,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/runtime-sofa-boot-starter/src/test/resources/config/application.properties b/runtime-sofa-boot-starter/src/test/resources/config/application.properties
index 86e431487..36e2852eb 100644
--- a/runtime-sofa-boot-starter/src/test/resources/config/application.properties
+++ b/runtime-sofa-boot-starter/src/test/resources/config/application.properties
@@ -1 +1,4 @@
spring.application.name=runtime-test
+mix-xml-annotation-unique-id=xmlAnnotationSampleService
+
+logging.path=./logs
\ No newline at end of file