diff --git a/CHANGES.txt b/CHANGES.txt
index e424c616e..a06fee9c8 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -313,3 +313,5 @@
其它valve可以通过pipelineContext.getAttribute("screenResult")来取得该值。
* Bugfix: 对于ClassNameWildcard和PathNameWildcard,当pattern以?开头或结束的时候,错误地匹配了多个字符(?的意思是有且仅有一个字符)
+
+* 新增 valve,用来将screen所返回的对象转换成json并输出到response。
diff --git a/CHANGES_SINCE_3.1.0.txt b/CHANGES_SINCE_3.1.0.txt
index 3dbf36d92..c4a1a4d9d 100644
--- a/CHANGES_SINCE_3.1.0.txt
+++ b/CHANGES_SINCE_3.1.0.txt
@@ -56,3 +56,5 @@
其它valve可以通过pipelineContext.getAttribute("screenResult")来取得该值。
* Bugfix: 对于ClassNameWildcard和PathNameWildcard,当pattern以?开头或结束的时候,错误地匹配了多个字符(?的意思是有且仅有一个字符)
+
+* 新增 valve,用来将screen所返回的对象转换成json并输出到response。
diff --git a/pom.xml b/pom.xml
index 3c2b0451e..5aa5029db 100644
--- a/pom.xml
+++ b/pom.xml
@@ -487,6 +487,11 @@
xml-apis
1.4.01
+
+ com.alibaba
+ fastjson
+ 1.1.23
+
diff --git a/webx/turbine/pom.xml b/webx/turbine/pom.xml
index 2801079d9..9046416ee 100644
--- a/webx/turbine/pom.xml
+++ b/webx/turbine/pom.xml
@@ -16,6 +16,10 @@
javax.servlet
servlet-api
+
+ com.alibaba
+ fastjson
+
${project.groupId}
citrus-webx-framework
diff --git a/webx/turbine/src/main/java/com/alibaba/citrus/turbine/pipeline/valve/PerformScreenValve.java b/webx/turbine/src/main/java/com/alibaba/citrus/turbine/pipeline/valve/PerformScreenValve.java
index 6bde0aab4..2379c111e 100644
--- a/webx/turbine/src/main/java/com/alibaba/citrus/turbine/pipeline/valve/PerformScreenValve.java
+++ b/webx/turbine/src/main/java/com/alibaba/citrus/turbine/pipeline/valve/PerformScreenValve.java
@@ -50,7 +50,7 @@
* @author Michael Zhou
*/
public class PerformScreenValve extends AbstractValve {
- private static final String DEFAULT_RESULT_NAME = "screenResult";
+ static final String DEFAULT_RESULT_NAME = "screenResult";
@Autowired
private ModuleLoaderService moduleLoaderService;
diff --git a/webx/turbine/src/main/java/com/alibaba/citrus/turbine/pipeline/valve/RenderResultAsJsonValve.java b/webx/turbine/src/main/java/com/alibaba/citrus/turbine/pipeline/valve/RenderResultAsJsonValve.java
new file mode 100644
index 000000000..8c7e15f91
--- /dev/null
+++ b/webx/turbine/src/main/java/com/alibaba/citrus/turbine/pipeline/valve/RenderResultAsJsonValve.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2002-2012 Alibaba Group Holding Limited.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.citrus.turbine.pipeline.valve;
+
+import static com.alibaba.citrus.springext.util.SpringExtUtil.*;
+import static com.alibaba.citrus.util.StringUtil.*;
+
+import java.io.PrintWriter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.citrus.service.pipeline.PipelineContext;
+import com.alibaba.citrus.service.pipeline.Valve;
+import com.alibaba.citrus.service.pipeline.support.AbstractValveDefinitionParser;
+import com.alibaba.fastjson.JSON;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+
+/**
+ * 将screen所返回的结果转换成json格式并输出。
+ *
+ * @author Michael Zhou
+ */
+public class RenderResultAsJsonValve implements Valve {
+ private static final String DEFAULT_RESULT_NAME = PerformScreenValve.DEFAULT_RESULT_NAME;
+ private static final String DEFAULT_CONTENT_TYPE = "application/json";
+ private static final String DEFAULT_JAVASCRIPT_VARIABLE = null;
+ private static final String DEFAULT_JAVASCRIPT_CONTENT_TYPE = "application/javascript";
+
+ @Autowired
+ private HttpServletRequest request;
+
+ @Autowired
+ private HttpServletResponse response;
+
+ private String resultName;
+ private String contentType;
+ private String javascriptVariable;
+ private String javascriptContentType;
+
+ public String getResultName() {
+ return resultName == null ? DEFAULT_RESULT_NAME : resultName;
+ }
+
+ public void setResultName(String resultName) {
+ this.resultName = trimToNull(resultName);
+ }
+
+ public String getContentType() {
+ return contentType == null ? DEFAULT_CONTENT_TYPE : contentType;
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = trimToNull(contentType);
+ }
+
+ public String getJavascriptVariable() {
+ return javascriptVariable == null ? DEFAULT_JAVASCRIPT_VARIABLE : javascriptVariable;
+ }
+
+ public void setJavascriptVariable(String javascriptVariable) {
+ this.javascriptVariable = trimToNull(javascriptVariable);
+ }
+
+ public String getJavascriptContentType() {
+ return javascriptContentType == null ? DEFAULT_JAVASCRIPT_CONTENT_TYPE : javascriptContentType;
+ }
+
+ public void setJavascriptContentType(String javascriptContentType) {
+ this.javascriptContentType = trimToNull(javascriptContentType);
+ }
+
+ public void invoke(PipelineContext pipelineContext) throws Exception {
+ String javascriptVariable = getJavascriptVariable();
+ boolean outputAsJson = javascriptVariable == null;
+
+ if (outputAsJson) {
+ // output as json
+ response.setContentType(getContentType());
+ } else {
+ // output as javascript
+ response.setContentType(getJavascriptContentType());
+ }
+
+ PrintWriter out = response.getWriter();
+ Object resultObject = pipelineContext.getAttribute(getResultName());
+ String jsonResult = JSON.toJSONString(resultObject);
+
+ if (outputAsJson) {
+ out.print(jsonResult);
+ } else {
+ out.print("var ");
+ out.print(javascriptVariable);
+ out.print(" = ");
+ out.print(jsonResult);
+ out.print(";");
+ }
+ }
+
+ public static class DefinitionParser extends AbstractValveDefinitionParser {
+ @Override
+ protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ attributesToProperties(element, builder, "resultName", "contentType", "javascriptVariable", "javascriptContentType");
+ }
+ }
+}
diff --git a/webx/turbine/src/main/resources/META-INF/services-pipeline-valves.bean-definition-parsers b/webx/turbine/src/main/resources/META-INF/services-pipeline-valves.bean-definition-parsers
index 7eb1faa54..c89ab0864 100644
--- a/webx/turbine/src/main/resources/META-INF/services-pipeline-valves.bean-definition-parsers
+++ b/webx/turbine/src/main/resources/META-INF/services-pipeline-valves.bean-definition-parsers
@@ -12,6 +12,7 @@ performAction=com.alibaba.citrus.turbine.pipeline.valve.PerformActionValve$Defin
performTemplateScreen=com.alibaba.citrus.turbine.pipeline.valve.PerformTemplateScreenValve$DefinitionParser
performScreen=com.alibaba.citrus.turbine.pipeline.valve.PerformScreenValve$DefinitionParser
renderTemplate=com.alibaba.citrus.turbine.pipeline.valve.RenderTemplateValve$DefinitionParser
+renderResultAsJson=com.alibaba.citrus.turbine.pipeline.valve.RenderResultAsJsonValve$DefinitionParser
exportControl=com.alibaba.citrus.turbine.pipeline.valve.ExportControlValve$DefinitionParser
breakUnlessTargetRedirected=com.alibaba.citrus.turbine.pipeline.valve.BreakUnlessTargetRedirectedValve$DefinitionParser
diff --git a/webx/turbine/src/main/resources/META-INF/services/pipeline/valves/renderResultAsJson.xsd b/webx/turbine/src/main/resources/META-INF/services/pipeline/valves/renderResultAsJson.xsd
new file mode 100644
index 000000000..73432bb1a
--- /dev/null
+++ b/webx/turbine/src/main/resources/META-INF/services/pipeline/valves/renderResultAsJson.xsd
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webx/turbine/src/test/config/WEB-INF/webx-app1.xml b/webx/turbine/src/test/config/WEB-INF/webx-app1.xml
index 9262c5100..873a618fd 100644
--- a/webx/turbine/src/test/config/WEB-INF/webx-app1.xml
+++ b/webx/turbine/src/test/config/WEB-INF/webx-app1.xml
@@ -55,6 +55,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webx/turbine/src/test/java/com/alibaba/citrus/turbine/pipeline/valve/RenderResultAsJsonValveTests.java b/webx/turbine/src/test/java/com/alibaba/citrus/turbine/pipeline/valve/RenderResultAsJsonValveTests.java
new file mode 100644
index 000000000..51c740223
--- /dev/null
+++ b/webx/turbine/src/test/java/com/alibaba/citrus/turbine/pipeline/valve/RenderResultAsJsonValveTests.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2002-2012 Alibaba Group Holding Limited.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.citrus.turbine.pipeline.valve;
+
+import static org.junit.Assert.*;
+
+import com.alibaba.citrus.service.pipeline.impl.PipelineImpl;
+import com.meterware.httpunit.WebResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RenderResultAsJsonValveTests extends AbstractValveTests {
+ @Before
+ public void init() {
+ pipeline = (PipelineImpl) factory.getBean("renderJson");
+ assertNotNull(pipeline);
+ }
+
+ @Test
+ public void outputAsJson_noResult() throws Exception {
+ getInvocationContext("http://localhost/app1/myJsonScreen/noResult");
+ initRequestContext();
+ pipeline.newInvocation().invoke();
+ commitRequestContext();
+
+ WebResponse webResponse = client.getResponse(invocationContext);
+
+ assertEquals(200, webResponse.getResponseCode());
+ assertEquals("application/json", webResponse.getContentType());
+ assertEquals("null", webResponse.getText());
+ }
+
+ @Test
+ public void outputAsJson_withResult() throws Exception {
+ getInvocationContext("http://localhost/app1/myJsonScreen/withResult");
+ initRequestContext();
+ pipeline.newInvocation().invoke();
+ commitRequestContext();
+
+ WebResponse webResponse = client.getResponse(invocationContext);
+
+ assertEquals(200, webResponse.getResponseCode());
+ assertEquals("application/json", webResponse.getContentType());
+ assertEquals("{\"age\":100,\"name\":\"michael\"}", webResponse.getText());
+ }
+
+ @Test
+ public void outputAsJson_withResult_specifiedContentType() throws Exception {
+ pipeline = (PipelineImpl) factory.getBean("renderJson_specifiedContentType");
+ getInvocationContext("http://localhost/app1/myJsonScreen/withResult");
+ initRequestContext();
+ pipeline.newInvocation().invoke();
+ commitRequestContext();
+
+ WebResponse webResponse = client.getResponse(invocationContext);
+
+ assertEquals(200, webResponse.getResponseCode());
+ assertEquals("text/plain", webResponse.getContentType());
+ assertEquals("{\"age\":100,\"name\":\"michael\"}", webResponse.getText());
+ }
+
+ @Test
+ public void outputAsJs_noResult() throws Exception {
+ pipeline = (PipelineImpl) factory.getBean("renderJsonAsJs");
+ getInvocationContext("http://localhost/app1/myJsonScreen/noResult");
+ initRequestContext();
+ pipeline.newInvocation().invoke();
+ commitRequestContext();
+
+ WebResponse webResponse = client.getResponse(invocationContext);
+
+ assertEquals(200, webResponse.getResponseCode());
+ assertEquals("application/javascript", webResponse.getContentType());
+ assertEquals("var myresult = null;", webResponse.getText());
+ }
+
+ @Test
+ public void outputAsJs_withResult() throws Exception {
+ pipeline = (PipelineImpl) factory.getBean("renderJsonAsJs");
+ getInvocationContext("http://localhost/app1/myJsonScreen/withResult");
+ initRequestContext();
+ pipeline.newInvocation().invoke();
+ commitRequestContext();
+
+ WebResponse webResponse = client.getResponse(invocationContext);
+
+ assertEquals(200, webResponse.getResponseCode());
+ assertEquals("application/javascript", webResponse.getContentType());
+ assertEquals("var myresult = {\"age\":100,\"name\":\"michael\"};", webResponse.getText());
+ }
+
+ @Test
+ public void outputAsJs_withResult_specifiedContentType() throws Exception {
+ pipeline = (PipelineImpl) factory.getBean("renderJsonAsJs_specifiedContentType");
+ getInvocationContext("http://localhost/app1/myJsonScreen/withResult");
+ initRequestContext();
+ pipeline.newInvocation().invoke();
+ commitRequestContext();
+
+ WebResponse webResponse = client.getResponse(invocationContext);
+
+ assertEquals(200, webResponse.getResponseCode());
+ assertEquals("text/js", webResponse.getContentType());
+ assertEquals("var myresult = {\"age\":100,\"name\":\"michael\"};", webResponse.getText());
+ }
+}
diff --git a/webx/turbine/src/test/java/com/alibaba/test/app1/module/screen/MyJsonScreen.java b/webx/turbine/src/test/java/com/alibaba/test/app1/module/screen/MyJsonScreen.java
new file mode 100644
index 000000000..852ea73eb
--- /dev/null
+++ b/webx/turbine/src/test/java/com/alibaba/test/app1/module/screen/MyJsonScreen.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2002-2012 Alibaba Group Holding Limited.
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.test.app1.module.screen;
+
+public class MyJsonScreen {
+ public void doNoResult() {
+ }
+
+ public Object doWithResult() throws Exception {
+ return new MyObject("michael", 100);
+ }
+
+ public static class MyObject {
+ private String name;
+ private int age;
+
+ public MyObject(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+ }
+}