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