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 }