View Javadoc

1   /*
2    * Copyright 2004-2008 the Seasar Foundation and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.seasar.cubby.action;
17  
18  import java.io.Writer;
19  import java.lang.reflect.Method;
20  import java.util.Collection;
21  import java.util.Map;
22  
23  import javax.servlet.http.HttpServletRequest;
24  import javax.servlet.http.HttpServletResponse;
25  
26  import org.seasar.framework.util.JSONSerializer;
27  import org.seasar.framework.util.StringUtil;
28  
29  /**
30   * JSON 形式のレスポンスを返す {@link ActionResult} です。
31   * <p>
32   * アクションメソッドの戻り値としてこのインスタンスを指定することで、指定された JavaBean を JSON/JSONP
33   * 形式に変換してレスポンスを返します。 ブラウザの JavaScript から発行されたリクエストを処理する場合等に使用してください。 JavaBean/
34   * {@link Map}/配列/{@link Collection}などがコンストラクタに渡すことができます。
35   * </p>
36   * <p>
37   * 使用例1 : JSON 形式のレスポンスを返す
38   * 
39   * <pre>
40   * MyBean bean = ...;
41   * return new Json(bean);
42   * </pre>
43   * 
44   * </p>
45   * <p>
46   * 使用例2 : コールバック関数名を指定して JSONP 形式のレスポンスを返す
47   * 
48   * <pre>
49   * MyBean bean = ...;
50   * return new Json(bean, &quot;callback&quot;);
51   * </pre>
52   * 
53   * </p>
54   * <p>
55   * 使用例3 : コンテントタイプと文字コードを指定して JSON 形式のレスポンスを返す。<br>
56   * セットされるコンテントタイプは"text/javascript+json; charset=Shift_JIS"になります。
57   * 
58   * <pre>
59   * MyBean bean = ...;
60   * return new Json(bean).contentType(&quot;text/javascript+json&quot;).encoding(&quot;Shift_JIS&quot;);
61   * </pre>
62   * 
63   * </p>
64   * 
65   * @see <a href="http://www.json.org/">JSON(JavaScript Object Notation)< /a>
66   * @see <a href="http://ajaxian.com/archives/jsonp-json-with-padding">JSONP(JSON
67   *      * with Padding)< /a>
68   * @see JSONSerializer#serialize(Object)
69   * @author baba
70   * @author agata
71   * @since 1.0.0
72   */
73  public class Json implements ActionResult {
74  
75  	private Object bean;
76  
77  	private String calllback;
78  
79  	private String contentType = "text/javascript";
80  
81  	private String encoding = "utf-8";
82  
83  	/**
84  	 * JSON 形式でレスポンスを返すインスタンスを生成します。
85  	 * 
86  	 * @param bean
87  	 *            JSON 形式に変換する JavaBean/{@link Map}/配列/{@link Collection}など
88  	 */
89  	public Json(final Object bean) {
90  		this(bean, null);
91  	}
92  
93  	/**
94  	 * JSONP 形式でレスポンスを返すインスタンスを生成します。
95  	 * 
96  	 * @param bean
97  	 *            JSONP 形式に変換する JavaBean/{@link Map}/配列/{@link Collection}など
98  	 * @param callback
99  	 *            コールバック関数名
100 	 */
101 	public Json(final Object bean, final String callback) {
102 		this.bean = bean;
103 		this.calllback = callback;
104 	}
105 
106 	/**
107 	 * JSON 形式に変換する JavaBeanを取得します。
108 	 * 
109 	 * @return JSON 形式に変換する JavaBean
110 	 */
111 	public Object getBean() {
112 		return this.bean;
113 	}
114 
115 	/**
116 	 * コールバック関数名を取得します。
117 	 * 
118 	 * @return コールバック関数名
119 	 */
120 	public String getCallback() {
121 		return this.calllback;
122 	}
123 
124 	/**
125 	 * コンテントタイプをセットします。
126 	 * 
127 	 * @param contentType
128 	 *            コンテントタイプ。(例:"text/javascript+json")
129 	 * @return {@link Json}
130 	 */
131 	public Json contentType(String contentType) {
132 		this.contentType = contentType;
133 		return this;
134 	}
135 
136 	/**
137 	 * コンテントタイプを取得します。
138 	 * 
139 	 * @return コンテントタイプ
140 	 */
141 	public String getContentType() {
142 		return this.contentType;
143 	}
144 
145 	/**
146 	 * エンコーディングをセットします。
147 	 * <p>
148 	 * セットされたエンコーディングはコンテントタイプのcharsetとして使用されます。
149 	 * </p>
150 	 * 
151 	 * @param encoding
152 	 *            エンコーディング。 (例:"Shift_JIS" )
153 	 * @return {@link Json}
154 	 */
155 	public Json encoding(String encoding) {
156 		this.encoding = encoding;
157 		return this;
158 	}
159 
160 	/**
161 	 * エンコーディングを取得します。
162 	 * 
163 	 * @return エンコーディング
164 	 */
165 	public String getEncoding() {
166 		return this.encoding;
167 	}
168 
169 	/**
170 	 * {@inheritDoc}
171 	 */
172 	public void execute(final Action action,
173 			final Class<? extends Action> actionClass, final Method method,
174 			final HttpServletRequest request, final HttpServletResponse response)
175 			throws Exception {
176 		response.setCharacterEncoding(this.encoding);
177 		response
178 				.setContentType(this.contentType + "; charset=" + this.encoding);
179 		response.setHeader("Cache-Control", "no-cache");
180 		response.setHeader("Pragma", "no-cache");
181 
182 		final String script;
183 		if (isJsonp()) {
184 			script = appendCallbackFunction(JSONSerializer.serialize(bean),
185 					calllback);
186 		} else {
187 			script = JSONSerializer.serialize(bean);
188 		}
189 
190 		final Writer writer = response.getWriter();
191 		writer.write(script);
192 		writer.flush();
193 	}
194 
195 	private boolean isJsonp() {
196 		return !StringUtil.isEmpty(calllback);
197 	}
198 
199 	private static String appendCallbackFunction(final String script,
200 			final String callback) {
201 		final StringBuilder builder = new StringBuilder(script.length()
202 				+ callback.length() + 10);
203 		builder.append(callback);
204 		builder.append("(");
205 		builder.append(script);
206 		builder.append(");");
207 		return builder.toString();
208 	}
209 }