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.internal.controller;
    17
    18  import java.util.Map;
    19  import java.util.ResourceBundle;
    20
    21  import javax.servlet.http.HttpServletRequest;
    22  import javax.servlet.http.HttpServletResponse;
    23
    24  import org.seasar.cubby.controller.MessagesBehaviour;
    25  import org.seasar.cubby.spi.ContainerProvider;
    26  import org.seasar.cubby.spi.ProviderFactory;
    27  import org.seasar.cubby.spi.container.Container;
    28
    29  /**
    30   * 実行スレッドのコンテキスト情報です。
    31   *
    32   * @author baba
    33   */
    34  public class ThreadContext {
    35
    36  	/** ThreadContext を保存するスレッドローカル。 */
    37  	private static final ThreadLocal<ThreadContext> THREAD_LOCAL = new ThreadLocal<ThreadContext>();
    38
    39  	/** 要求。 */
    40  	private final HttpServletRequest request;
    41
    42  	/** 応答 */
    43  	private final HttpServletResponse response;
    44
    45  	/** メッセージのリソースバンドル。 */
    46  	private ResourceBundle messagesResourceBundle = null;
    47
    48  	/** メッセージの {@link Map} */
    49  	private Map<String, Object> messages = null;
    50
    51  	/** メッセージ表示用リソースバンドルの振る舞い。 */
    52  	private MessagesBehaviour messagesBehaviour;
    53
    54  	/**
    55  	 * インスタンス化します。
    56  	 *
    57  	 * @param request
    58  	 *            要求
    59  	 * @param response
    60  	 *            応答
    61  	 */
    62  	private ThreadContext(final HttpServletRequest request,
    63  			final HttpServletResponse response) {
    64  		this.request = request;
    65  		this.response = response;
    66  	}
    67
    68  	/**
    69  	 * スレッドローカル変数からコンテキストを取得します。
    70  	 *
    71  	 * @return コンテキスト
    72  	 */
    73  	private static ThreadContext getContext() {
    74  		final ThreadContext context = THREAD_LOCAL.get();
    75  		if (context == null) {
    76  			throw new IllegalStateException(
    77  					"Could not get context from ThreadLocal. run in context scope.");
    78  		}
    79  		return context;
    80  	}
    81
    82  	/**
    83  	 * 現在の実行スレッドに関連付けられた要求を取得します。
    84  	 *
    85  	 * @return 要求
    86  	 */
    87  	public static HttpServletRequest getRequest() {
    88  		return getContext().request;
    89  	}
    90
    91  	/**
    92  	 * 現在の実行スレッドに関連付けられた応答を取得します。
    93  	 *
    94  	 * @return 応答
    95  	 */
    96  	public static HttpServletResponse getResponse() {
    97  		return getContext().response;
    98  	}
    99
    100 	/**
    101 	 * 現在の実行スレッドに関連付けられた要求に対応するメッセージ用の {@link ResourceBundle} を取得します。
    102 	 *
    103 	 * @return リソースバンドル
    104 	 */
    105 	public static ResourceBundle getMessagesResourceBundle() {
    106 		final ThreadContext context = getContext();
    107 		if (context.messagesResourceBundle == null) {
    108 			final MessagesBehaviour messagesBehaviour = getMessagesBehaviour(context);
    109 			context.messagesResourceBundle = messagesBehaviour
    110 					.getBundle(context.request.getLocale());
    111 		}
    112 		return context.messagesResourceBundle;
    113 	}
    114
    115 	/**
    116 	 * {@link #getMessagesResourceBundle()} で取得できる {@link ResourceBundle} を変換した
    117 	 * {@link Map} を取得します。
    118 	 *
    119 	 * @return メッセージの {@link Map}
    120 	 */
    121 	public static Map<String, Object> getMessagesMap() {
    122 		final ThreadContext context = getContext();
    123 		if (context.messages == null) {
    124 			final ResourceBundle bundle = getMessagesResourceBundle();
    125 			final MessagesBehaviour messagesBehaviour = getMessagesBehaviour(context);
    126 			context.messages = messagesBehaviour.toMap(bundle);
    127 		}
    128 		return context.messages;
    129 	}
    130
    131 	/**
    132 	 * メッセージ表示用リソースバンドルの振る舞いを取得します。
    133 	 *
    134 	 * @param context
    135 	 *            実行スレッドのコンテキスト情報
    136 	 * @return メッセージ表示用リソースバンドルの振る舞い
    137 	 */
    138 	private static MessagesBehaviour getMessagesBehaviour(
    139 			final ThreadContext context) {
    140 		if (context.messagesBehaviour == null) {
    141 			final Container container = ProviderFactory.get(
    142 					ContainerProvider.class).getContainer();
    143 			context.messagesBehaviour = container
    144 					.lookup(MessagesBehaviour.class);
    145 		}
    146 		return context.messagesBehaviour;
    147 	}
    148
    149 	/**
    150 	 * 指定されたコマンドを新しいコンテキスト内で実行します。
    151 	 *
    152 	 * @param request
    153 	 *            要求
    154 	 * @param response
    155 	 *            応答
    156 	 * @param command
    157 	 *            コンテキスト内で実行するコマンド
    158 	 * @throws Exception
    159 	 *             コマンドの実行中に例外が発生した場合
    160 	 */
    161 	public static void runInContext(final HttpServletRequest request,
    162 			final HttpServletResponse response, final Command command)
    163 			throws Exception {
    164 		if (request == null) {
    165 			throw new NullPointerException("request");
    166 		}
    167 		if (response == null) {
    168 			throw new NullPointerException("response");
    169 		}
    170 		if (command == null) {
    171 			throw new NullPointerException("command");
    172 		}
    173
    174 		final ThreadContext previous = THREAD_LOCAL.get();
    175 		final ThreadContext context = new ThreadContext(request, response);
    176 		THREAD_LOCAL.set(context);
    177 		try {
    178 			command.execute(request, response);
    179 		} finally {
    180 			if (previous == null) {
    181 				THREAD_LOCAL.remove();
    182 			} else {
    183 				THREAD_LOCAL.set(previous);
    184 			}
    185 		}
    186 	}
    187
    188 	/**
    189 	 * コンテキスト内で実行するコマンドのインターフェイスです。
    190 	 *
    191 	 * @author baba
    192 	 */
    193 	public interface Command {
    194
    195 		/**
    196 		 * コマンドを実行します。
    197 		 *
    198 		 * @param request
    199 		 *            要求
    200 		 * @param response
    201 		 *            応答
    202 		 * @throws Exception
    203 		 *             コマンドの実行中に例外が発生した場合
    204 		 */
    205 		void execute(HttpServletRequest request, HttpServletResponse response)
    206 				throws Exception;
    207
    208 	}
    209
    210 }