You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by wu...@apache.org on 2018/06/14 12:23:47 UTC

[incubator-servicecomb-java-chassis] branch master updated: [SCB-625] ProduceProcessor use SPI to support extends (#741)

This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new ec3ec37  [SCB-625] ProduceProcessor use SPI to support extends (#741)
ec3ec37 is described below

commit ec3ec37e9fa18d1b2ec310f9d749b617d0938d4e
Author: weichao <30...@users.noreply.github.com>
AuthorDate: Thu Jun 14 20:23:44 2018 +0800

    [SCB-625] ProduceProcessor use SPI to support extends (#741)
    
    * [SCB-625] ProduceProcessor use SPI to support extends
    
    * [SCB-625] ProduceProcessor use SPI to support extends
    
    * [SCB-625] ProduceProcessor use SPI to support extends
    
    * [SCB-625] ProduceProcessor use SPI to support extends,use SPIServiceUtils
    
    * [SCB-625] ProduceProcessor use SPI to support extends,use SPIServiceUtils
    
    * [SCB-625] ProduceProcessor use SPI to support extends,use SPIServiceUtils
    
    * [SCB-625] rebase,make getOrder useful and add demo
    
    * [SCB-625] rebase
    
    * [SCB-625] amend last commit
    
    * [SCB-625] amend last commit
---
 .../codec/produce/AbstractProduceProcessor.java    | 24 ------
 .../rest/codec/produce/ProduceJsonProcessor.java   |  7 +-
 .../rest/codec/produce/ProduceProcessor.java       |  2 +
 .../codec/produce/ProduceProcessorManager.java     | 18 ++++-
 .../codec/produce/ProduceTextPlainProcessor.java   |  7 +-
 ...comb.common.rest.codec.produce.ProduceProcessor | 19 +++++
 .../demo/jaxrs/server/CodeFirstJaxrs.java          |  9 +++
 .../servicecomb/demo/CodeFirstRestTemplate.java    | 22 +++++-
 .../apache/servicecomb/demo/jaxbbean/JAXBJob.java  | 45 +++++++++++
 .../servicecomb/demo/jaxbbean/JAXBPerson.java      | 92 ++++++++++++++++++++++
 .../produceprocessor/ProduceAppXmlProcessor.java   | 35 ++++++++
 .../override/ProduceAppXmlProcessor.java           | 35 ++++++++
 .../apache/servicecomb/demo/utils/JAXBUtils.java   | 75 ++++++++++++++++++
 ...comb.common.rest.codec.produce.ProduceProcessor | 19 +++++
 .../demo/springmvc/server/CodeFirstSpringmvc.java  |  6 ++
 15 files changed, 386 insertions(+), 29 deletions(-)

diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/AbstractProduceProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/AbstractProduceProcessor.java
deleted file mode 100644
index 7704fa2..0000000
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/AbstractProduceProcessor.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 org.apache.servicecomb.common.rest.codec.produce;
-
-public abstract class AbstractProduceProcessor implements ProduceProcessor {
-  public AbstractProduceProcessor() {
-    ProduceProcessorManager.INSTANCE.register(getName(), this);
-  }
-}
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java
index 28e2b29..73cbf62 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java
@@ -26,7 +26,7 @@ import org.apache.servicecomb.common.rest.codec.RestObjectMapper;
 
 import com.fasterxml.jackson.databind.JavaType;
 
-public class ProduceJsonProcessor extends AbstractProduceProcessor {
+public class ProduceJsonProcessor implements ProduceProcessor {
 
   @Override
   public String getName() {
@@ -42,4 +42,9 @@ public class ProduceJsonProcessor extends AbstractProduceProcessor {
   public Object doDecodeResponse(InputStream input, JavaType type) throws Exception {
     return RestObjectMapper.INSTANCE.readValue(input, type);
   }
+
+  @Override
+  public int getOrder() {
+    return 0;
+  }
 }
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java
index 244c901..127250f 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java
@@ -30,6 +30,8 @@ import io.vertx.core.buffer.Buffer;
 public interface ProduceProcessor {
   String getName();
 
+  int getOrder();
+
   default void encodeResponse(OutputStream output, Object result) throws Exception {
     if (result == null) {
       return;
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java
index d70eac4..255b35d 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java
@@ -17,24 +17,38 @@
 
 package org.apache.servicecomb.common.rest.codec.produce;
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import javax.ws.rs.core.MediaType;
 
 import org.apache.servicecomb.foundation.common.RegisterManager;
+import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 
 public final class ProduceProcessorManager extends RegisterManager<String, ProduceProcessor> {
+  private static final List<ProduceProcessor> produceProcessor =
+      SPIServiceUtils.getSortedService(ProduceProcessor.class);
+
   private static final String NAME = "produce processor mgr";
 
   public static final String DEFAULT_TYPE = MediaType.APPLICATION_JSON;
 
   public static final ProduceProcessorManager INSTANCE = new ProduceProcessorManager();
 
-  public static final ProduceProcessor JSON_PROCESSOR = new ProduceJsonProcessor();
+  public static final ProduceProcessor JSON_PROCESSOR =
+      SPIServiceUtils.getTargetService(ProduceProcessor.class, ProduceJsonProcessor.class);
 
-  public static final ProduceProcessor PLAIN_PROCESSOR = new ProduceTextPlainProcessor();
+  public static final ProduceProcessor PLAIN_PROCESSOR =
+      SPIServiceUtils.getTargetService(ProduceProcessor.class, ProduceTextPlainProcessor.class);
 
   public static final ProduceProcessor DEFAULT_PROCESSOR = JSON_PROCESSOR;
 
   private ProduceProcessorManager() {
     super(NAME);
+    Set<String> set = new HashSet<>();
+    produceProcessor.forEach(processor -> {
+      if (set.add(processor.getName()))
+        register(processor.getName(), processor);
+    });
   }
 }
diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java
index 7157adf..0311182 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java
@@ -27,7 +27,7 @@ import org.apache.commons.io.IOUtils;
 
 import com.fasterxml.jackson.databind.JavaType;
 
-public class ProduceTextPlainProcessor extends AbstractProduceProcessor {
+public class ProduceTextPlainProcessor implements ProduceProcessor {
   @Override
   public String getName() {
     return MediaType.TEXT_PLAIN;
@@ -59,4 +59,9 @@ public class ProduceTextPlainProcessor extends AbstractProduceProcessor {
     //                    .newInstance((String)result);
     //        }
   }
+
+  @Override
+  public int getOrder() {
+    return 0;
+  }
 }
diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor
new file mode 100644
index 0000000..9353b9d
--- /dev/null
+++ b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor
+org.apache.servicecomb.common.rest.codec.produce.ProduceTextPlainProcessor
\ No newline at end of file
diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java
index 35cdd56..11987e3 100644
--- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java
+++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java
@@ -42,6 +42,7 @@ import org.apache.servicecomb.core.Const;
 import org.apache.servicecomb.demo.compute.Person;
 import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore;
 import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore;
+import org.apache.servicecomb.demo.jaxbbean.JAXBPerson;
 import org.apache.servicecomb.demo.server.User;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
 import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody;
@@ -93,6 +94,14 @@ public class CodeFirstJaxrs {
     return body;
   }
 
+  @Path("/appXml")
+  @POST
+  @Consumes(MediaType.APPLICATION_JSON)
+  @Produces(MediaType.APPLICATION_XML)
+  public JAXBPerson appXml(JAXBPerson body) {
+    return body;
+  }
+
   @Path("/bytes")
   @POST
   public byte[] bytes(byte[] input) {
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java
index d637672..d5a12be 100644
--- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java
@@ -21,11 +21,14 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager;
 import org.apache.servicecomb.core.Const;
 import org.apache.servicecomb.core.CseContext;
 import org.apache.servicecomb.demo.compute.Person;
 import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore;
 import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore;
+import org.apache.servicecomb.demo.jaxbbean.JAXBJob;
+import org.apache.servicecomb.demo.jaxbbean.JAXBPerson;
 import org.apache.servicecomb.demo.server.User;
 import org.apache.servicecomb.serviceregistry.RegistryUtils;
 import org.apache.servicecomb.swagger.invocation.context.ContextUtils;
@@ -34,6 +37,7 @@ import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.client.RestTemplate;
 
@@ -62,6 +66,7 @@ public class CodeFirstRestTemplate {
   protected void testAllTransport(String microserviceName, RestTemplate template, String cseUrlPrefix) {
     testCodeFirstUserMap(template, cseUrlPrefix);
     testCodeFirstTextPlain(template, cseUrlPrefix);
+    testCodeFirstAppXml(template, cseUrlPrefix);
     testCodeFirstBytes(template, cseUrlPrefix);
     testCseResponse(microserviceName, template, cseUrlPrefix);
     testCodeFirstAddDate(template, cseUrlPrefix);
@@ -123,6 +128,21 @@ public class CodeFirstRestTemplate {
     TestMgr.check(body, result);
   }
 
+  private void testCodeFirstAppXml(RestTemplate template, String cseUrlPrefix) {
+    JAXBPerson person = new JAXBPerson("jake", 22, "it", "60kg");
+    person.setJob(new JAXBJob("developer", "coding"));
+    HttpHeaders headers = new HttpHeaders();
+    headers.add("Accept", MediaType.APPLICATION_XML_VALUE);
+    headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
+    HttpEntity<JAXBPerson> requestEntity = new HttpEntity<>(person, headers);
+    ResponseEntity<JAXBPerson> resEntity = template.exchange(cseUrlPrefix + "appXml",
+        HttpMethod.POST,
+        requestEntity,
+        JAXBPerson.class);
+    TestMgr.check(-1, ProduceProcessorManager.INSTANCE.ensureFindValue(MediaType.APPLICATION_XML_VALUE).getOrder());
+    TestMgr.check(person, resEntity.getBody());
+  }
+
   private void testCodeFirstBytes(RestTemplate template, String cseUrlPrefix) {
     byte[] body = new byte[] {0, 1, 2};
     byte[] result = template.postForObject(cseUrlPrefix + "bytes",
@@ -240,7 +260,7 @@ public class CodeFirstRestTemplate {
   protected void testModelFieldIgnore(RestTemplate template, String cseUrlPrefix) {
     InputModelForTestIgnore input = new InputModelForTestIgnore("input_id_rest", "input_id_content",
         new Person("inputSomeone"), new JsonObject("{\"InputJsonKey\" : \"InputJsonValue\"}"), () -> {
-    });
+        });
     OutputModelForTestIgnore output = template
         .postForObject(cseUrlPrefix + "ignore", input, OutputModelForTestIgnore.class);
 
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java
new file mode 100644
index 0000000..b8da947
--- /dev/null
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java
@@ -0,0 +1,45 @@
+package org.apache.servicecomb.demo.jaxbbean;
+
+import javax.xml.bind.annotation.XmlAccessOrder;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorOrder;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "job")
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
+public class JAXBJob {
+  private String name;
+
+  private String content;
+
+  public JAXBJob() {
+  }
+
+  public JAXBJob(String name, String content) {
+    this.name = name;
+    this.content = content;
+  }
+
+  @Override
+  public String toString() {
+    return "Job{" + "name'" + name + '\'' + ", content='" + content + '\'' + '}';
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getContent() {
+    return content;
+  }
+
+  public void setContent(String content) {
+    this.content = content;
+  }
+}
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java
new file mode 100644
index 0000000..e69376d
--- /dev/null
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java
@@ -0,0 +1,92 @@
+package org.apache.servicecomb.demo.jaxbbean;
+
+import java.io.Serializable;
+
+import javax.xml.bind.annotation.XmlAccessOrder;
+import javax.xml.bind.annotation.XmlAccessorOrder;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlType(propOrder = {"name", "role", "job"})
+@XmlRootElement(name = "person")
+@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
+public class JAXBPerson implements Serializable {
+  private static final long serialVersionUID = -7127275268696924681L;
+
+  private String name;
+
+  private int age;
+
+  private String role;
+
+  private String weight;
+
+  private JAXBJob job;
+
+  public JAXBPerson() {
+  }
+
+  public JAXBPerson(String name, int age, String role, String weight) {
+    this.name = name;
+    this.age = age;
+    this.role = role;
+    this.weight = weight;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  @XmlAttribute
+  public int getAge() {
+    return age;
+  }
+
+  public void setAge(int age) {
+    this.age = age;
+  }
+
+  @XmlElement(nillable = true)
+  public String getRole() {
+    return role;
+  }
+
+  public void setRole(String role) {
+    this.role = role;
+  }
+
+  @XmlTransient
+  public String getWeight() {
+    return weight;
+  }
+
+  public void setWeight(String weight) {
+    this.weight = weight;
+  }
+
+  @XmlElement
+  public JAXBJob getJob() {
+    return job;
+  }
+
+  public void setJob(JAXBJob job) {
+    this.job = job;
+  }
+
+  @Override
+  public String toString() {
+    return "Person{" +
+        "name='" + name + '\'' +
+        ", age=" + age +
+        ", role='" + role + '\'' +
+        ", job=" + job +
+        '}';
+  }
+}
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java
new file mode 100644
index 0000000..db88297
--- /dev/null
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.java
@@ -0,0 +1,35 @@
+package org.apache.servicecomb.demo.produceprocessor;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
+import org.apache.servicecomb.demo.utils.JAXBUtils;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+public class ProduceAppXmlProcessor implements ProduceProcessor {
+
+  @Override
+  public String getName() {
+    return MediaType.APPLICATION_XML;
+  }
+
+  @Override
+  public int getOrder() {
+    return 0;
+  }
+
+  @Override
+  public void doEncodeResponse(OutputStream output, Object result) throws Exception {
+    output.write(JAXBUtils.convertToXml(result).getBytes());
+  }
+
+  @Override
+  public Object doDecodeResponse(InputStream input, JavaType type) throws Exception {
+    return JAXBUtils.convertToJavaBean(input, type);
+  }
+
+}
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java
new file mode 100644
index 0000000..fc1a01a
--- /dev/null
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.java
@@ -0,0 +1,35 @@
+package org.apache.servicecomb.demo.produceprocessor.override;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
+import org.apache.servicecomb.demo.utils.JAXBUtils;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+public class ProduceAppXmlProcessor implements ProduceProcessor {
+
+  @Override
+  public String getName() {
+    return MediaType.APPLICATION_XML;
+  }
+
+  @Override
+  public int getOrder() {
+    return -1;
+  }
+
+  @Override
+  public void doEncodeResponse(OutputStream output, Object result) throws Exception {
+    output.write(JAXBUtils.convertToXml(result).getBytes());
+  }
+
+  @Override
+  public Object doDecodeResponse(InputStream input, JavaType type) throws Exception {
+    return JAXBUtils.convertToJavaBean(input, type);
+  }
+
+}
diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java
new file mode 100644
index 0000000..511a18c
--- /dev/null
+++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java
@@ -0,0 +1,75 @@
+package org.apache.servicecomb.demo.utils;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.InputSource;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+public class JAXBUtils {
+  private static final Logger LOGGER = LoggerFactory.getLogger(JAXBUtils.class);
+
+  public static String convertToXml(Object obj) {
+    return convertToXml(obj, StandardCharsets.UTF_8.toString());
+  }
+
+  public static String convertToXml(Object obj, String encoding) {
+    String result = null;
+    try {
+      JAXBContext context = JAXBContext.newInstance(obj.getClass());
+      Marshaller marshaller = context.createMarshaller();
+      marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+      marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
+
+      StringWriter writer = new StringWriter();
+      marshaller.marshal(obj, writer);
+      result = writer.toString();
+    } catch (Exception e) {
+      LOGGER.error("Bean convert to xml failed, error message: {}", e.getMessage());
+    }
+    return result;
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> T convertToJavaBean(InputStream xml, JavaType type) {
+    return (T) convertToJavaBean(xml, type.getRawClass());
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> T convertToJavaBean(InputStream xml, Class<T> c) {
+    T t = null;
+    try {
+      JAXBContext context = JAXBContext.newInstance(c);
+      Unmarshaller unmarshaller = context.createUnmarshaller();
+      t = (T) unmarshaller.unmarshal(XXEPrevention(xml));
+    } catch (Exception e) {
+      LOGGER.error("Xml convert to Bean failed, error message: {}", e.getMessage());
+    }
+    return t;
+  }
+
+  private static Source XXEPrevention(InputStream xml) {
+    Source xmlSource = null;
+    SAXParserFactory spf = SAXParserFactory.newInstance();
+    try {
+      spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
+      spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+      spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+      xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(xml));
+    } catch (Exception e) {
+      LOGGER.error("Xml External Entity (XXE) Processing report error, error message: {}", e.getMessage());
+    }
+    return xmlSource;
+  }
+}
diff --git a/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor b/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor
new file mode 100644
index 0000000..ff9a6cb
--- /dev/null
+++ b/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+org.apache.servicecomb.demo.produceprocessor.ProduceAppXmlProcessor
+org.apache.servicecomb.demo.produceprocessor.override.ProduceAppXmlProcessor
\ No newline at end of file
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
index 8d96d9c..4e3ae8b 100644
--- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
@@ -37,6 +37,7 @@ import org.apache.servicecomb.demo.Generic;
 import org.apache.servicecomb.demo.compute.Person;
 import org.apache.servicecomb.demo.ignore.InputModelForTestIgnore;
 import org.apache.servicecomb.demo.ignore.OutputModelForTestIgnore;
+import org.apache.servicecomb.demo.jaxbbean.JAXBPerson;
 import org.apache.servicecomb.demo.server.User;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
 import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody;
@@ -155,6 +156,11 @@ public class CodeFirstSpringmvc {
     return body;
   }
 
+  @RequestMapping(path = "/appXml", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
+  public JAXBPerson appXml(@RequestBody JAXBPerson person) {
+    return person;
+  }
+
   @RequestMapping(path = "/bytes", method = RequestMethod.POST)
   public byte[] bytes(@RequestBody byte[] input) {
     input[0] = (byte) (input[0] + 1);

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.