View Javadoc

    1   /*
    2    * Copyright 2004-2009 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.util.Collection;
    20  import java.util.Map;
    21
    22  import javax.servlet.http.HttpServletRequest;
    23  import javax.servlet.http.HttpServletResponse;
    24
    25  import org.seasar.cubby.internal.util.StringUtils;
    26  import org.seasar.cubby.spi.JsonProvider;
    27  import org.seasar.cubby.spi.ProviderFactory;
    28
    29  /**
    30   * JSON 形式の応答を返す {@link ActionResult} です。
    31   * <p>
    32   * アクションメソッドの戻り値としてこのインスタンスを指定することで、指定された JavaBean を JSON/JSONP 形式に変換して応答を返します。
    33   * ブラウザの JavaScript から発行された要求を処理する場合等に使用してください。 JavaBean/ {@link Map}/配列/
    34   * {@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 : MIME タイプと文字コードを指定して JSON 形式の応答を返す。<br>
    56   * 設定される MIME タイプは"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
    66   *      href="http://www.json.org/">JSON(JavaScript&nbsp;Object&nbsp;Notation)</a>
    67   * @see <a
    68   *      href="http://ajaxian.com/archives/jsonp-json-with-padding">JSONP(JSON&nbsp;with&nbsp;Padding)</a>
    69   * @see JsonProvider#toJson(Object)
    70   * @author baba
    71   * @author agata
    72   */
    73  public class Json implements ActionResult {
    74
    75  	/** 変換対象のオブジェクト。 */
    76  	private final Object bean;
    77
    78  	/** 変換に使用する {@link JsonProvider}。 */
    79  	private final JsonProvider jsonProvider;
    80
    81  	/** コールバック関数名。 */
    82  	private final String calllback;
    83
    84  	/** MIME タイプ。 */
    85  	private String contentType = "text/javascript";
    86
    87  	/** エンコーディング。 */
    88  	private String encoding = "utf-8";
    89
    90  	/** X-JSON 応答ヘッダを使用するか。 */
    91  	private boolean xjson = false;
    92
    93  	/**
    94  	 * JSON 形式で応答を返すインスタンスを生成します。
    95  	 *
    96  	 * @param bean
    97  	 *            JSON 形式に変換するオブジェクト
    98  	 */
    99  	public Json(final Object bean) {
    100 		this(bean, null, ProviderFactory.get(JsonProvider.class));
    101 	}
    102
    103 	/**
    104 	 * JSON 形式で応答を返すインスタンスを生成します。
    105 	 *
    106 	 * @param bean
    107 	 *            JSON 形式に変換するオブジェクト
    108 	 * @param jsonProvider
    109 	 *            JSON のプロバイダ
    110 	 */
    111 	public Json(final Object bean, final JsonProvider jsonProvider) {
    112 		this(bean, null, jsonProvider);
    113 	}
    114
    115 	/**
    116 	 * JSONP 形式で応答を返すインスタンスを生成します。
    117 	 *
    118 	 * @param bean
    119 	 *            JSONP 形式に変換するオブジェクト
    120 	 * @param callback
    121 	 *            コールバック関数名
    122 	 */
    123 	public Json(final Object bean, final String callback) {
    124 		this(bean, callback, ProviderFactory.get(JsonProvider.class));
    125 	}
    126
    127 	/**
    128 	 * JSONP 形式で応答を返すインスタンスを生成します。
    129 	 *
    130 	 * @param bean
    131 	 *            JSONP 形式に変換するオブジェクト
    132 	 * @param callback
    133 	 *            コールバック関数名
    134 	 * @param jsonProvider
    135 	 *            JSON のプロバイダ
    136 	 */
    137 	public Json(final Object bean, final String callback,
    138 			final JsonProvider jsonProvider) {
    139 		if (jsonProvider == null) {
    140 			throw new NullPointerException("jsonProvider");
    141 		}
    142 		this.bean = bean;
    143 		this.calllback = callback;
    144 		this.jsonProvider = jsonProvider;
    145 	}
    146
    147 	/**
    148 	 * JSON 形式に変換する JavaBeanを取得します。
    149 	 *
    150 	 * @return JSON 形式に変換する JavaBean
    151 	 */
    152 	public Object getBean() {
    153 		return this.bean;
    154 	}
    155
    156 	/**
    157 	 * コールバック関数名を取得します。
    158 	 *
    159 	 * @return コールバック関数名
    160 	 */
    161 	public String getCallback() {
    162 		return this.calllback;
    163 	}
    164
    165 	/**
    166 	 * MIME タイプを設定します。
    167 	 *
    168 	 * @param contentType
    169 	 *            MIME タイプ (例："text/javascript+json")
    170 	 * @return {@link Json}
    171 	 */
    172 	public Json contentType(final String contentType) {
    173 		this.contentType = contentType;
    174 		return this;
    175 	}
    176
    177 	/**
    178 	 * MIME タイプを取得します。
    179 	 *
    180 	 * @return MIME タイプ
    181 	 */
    182 	public String getContentType() {
    183 		return this.contentType;
    184 	}
    185
    186 	/**
    187 	 * エンコーディングを設定します。
    188 	 * <p>
    189 	 * 設定されたエンコーディングは MIME タイプの charset として使用されます。
    190 	 * </p>
    191 	 *
    192 	 * @param encoding
    193 	 *            エンコーディング (例："Shift_JIS")
    194 	 * @return {@link Json}
    195 	 */
    196 	public Json encoding(final String encoding) {
    197 		this.encoding = encoding;
    198 		return this;
    199 	}
    200
    201 	/**
    202 	 * エンコーディングを取得します。
    203 	 *
    204 	 * @return エンコーディング
    205 	 */
    206 	public String getEncoding() {
    207 		return this.encoding;
    208 	}
    209
    210 	/**
    211 	 * JSON 文字列を応答ボディではなく X-JSON 応答ヘッダに設定することを指定します。
    212 	 * <p>
    213 	 * prototype.js の <code>Ajax.Request</code> を使うときに使用してください。
    214 	 * </p>
    215 	 *
    216 	 * @see <a
    217 	 *      href="http://www.prototypejs.org/api/ajax/options">www.prototypejs.org&nbsp;-&nbsp;Ajax&nbsp;Options</a>
    218 	 */
    219 	public void xjson() {
    220 		this.xjson = true;
    221 	}
    222
    223 	/**
    224 	 * JSON 文字列を X-JOSN 応答ヘッダに設定するかを示します。
    225 	 *
    226 	 * @return JSON 文字列を X-JOSN 応答ヘッダに設定する場合は <code>true</code>、そうでない場合は
    227 	 *         <code>false</code>
    228 	 */
    229 	public boolean isXjson() {
    230 		return xjson;
    231 	}
    232
    233 	/**
    234 	 * {@inheritDoc}
    235 	 */
    236 	public void execute(final ActionContext actionContext,
    237 			final HttpServletRequest request, final HttpServletResponse response)
    238 			throws Exception {
    239 		response.setCharacterEncoding(this.encoding);
    240 		response
    241 				.setContentType(this.contentType + "; charset=" + this.encoding);
    242 		response.setHeader("Cache-Control", "no-cache");
    243 		response.setHeader("Pragma", "no-cache");
    244
    245 		final String script;
    246 		if (isJsonp()) {
    247 			script = appendCallbackFunction(jsonProvider.toJson(bean),
    248 					calllback);
    249 		} else {
    250 			script = jsonProvider.toJson(bean);
    251 		}
    252
    253 		if (xjson) {
    254 			response.setHeader("X-JSON", script);
    255 		} else {
    256 			final Writer writer = response.getWriter();
    257 			writer.write(script);
    258 			writer.flush();
    259 		}
    260 	}
    261
    262 	/**
    263 	 * JSONP 形式に変換するかどうかを示します。
    264 	 *
    265 	 * @return JSONP 形式に変換する場合は <code>true</code>、そうでない場合は <code>false</code>
    266 	 */
    267 	private boolean isJsonp() {
    268 		return !StringUtils.isEmpty(calllback);
    269 	}
    270
    271 	/**
    272 	 * JSON 形式のスクリプトに指定されたコールバック関数を付加します。
    273 	 *
    274 	 * @param script
    275 	 *            JSON 形式のスクリプト
    276 	 * @param callback
    277 	 *            コールバック関数名
    278 	 * @return コールバック関数が追加された JSON 形式のスクリプト
    279 	 */
    280 	private static String appendCallbackFunction(final String script,
    281 			final String callback) {
    282 		final StringBuilder builder = new StringBuilder(script.length()
    283 				+ callback.length() + 10);
    284 		builder.append(callback);
    285 		builder.append("(");
    286 		builder.append(script);
    287 		builder.append(");");
    288 		return builder.toString();
    289 	}
    290
    291 }