View Javadoc

1   /*
2    * Copyright 2004-2010 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  
17  package org.seasar.cubby.plugins.guice;
18  
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashSet;
22  import java.util.LinkedHashSet;
23  import java.util.Set;
24  
25  import javax.servlet.http.HttpServletRequest;
26  
27  import org.seasar.cubby.action.ActionContext;
28  import org.seasar.cubby.action.ActionContextProxy;
29  import org.seasar.cubby.action.ActionErrors;
30  import org.seasar.cubby.action.ActionErrorsProxy;
31  import org.seasar.cubby.action.FlashMap;
32  import org.seasar.cubby.action.FlashMapProxy;
33  import org.seasar.cubby.controller.FormatPattern;
34  import org.seasar.cubby.controller.MessagesBehaviour;
35  import org.seasar.cubby.controller.RequestParser;
36  import org.seasar.cubby.controller.impl.DefaultFormatPattern;
37  import org.seasar.cubby.controller.impl.DefaultMessagesBehaviour;
38  import org.seasar.cubby.controller.impl.DefaultRequestParser;
39  import org.seasar.cubby.controller.impl.MultipartRequestParser;
40  import org.seasar.cubby.converter.Converter;
41  import org.seasar.cubby.converter.impl.BigDecimalConverter;
42  import org.seasar.cubby.converter.impl.BigIntegerConverter;
43  import org.seasar.cubby.converter.impl.BooleanConverter;
44  import org.seasar.cubby.converter.impl.ByteArrayFileItemConverter;
45  import org.seasar.cubby.converter.impl.ByteConverter;
46  import org.seasar.cubby.converter.impl.CharacterConverter;
47  import org.seasar.cubby.converter.impl.DateConverter;
48  import org.seasar.cubby.converter.impl.DoubleConverter;
49  import org.seasar.cubby.converter.impl.EnumConverter;
50  import org.seasar.cubby.converter.impl.FloatConverter;
51  import org.seasar.cubby.converter.impl.InputStreamFileItemConverter;
52  import org.seasar.cubby.converter.impl.IntegerConverter;
53  import org.seasar.cubby.converter.impl.LongConverter;
54  import org.seasar.cubby.converter.impl.ShortConverter;
55  import org.seasar.cubby.converter.impl.SqlDateConverter;
56  import org.seasar.cubby.converter.impl.SqlTimeConverter;
57  import org.seasar.cubby.converter.impl.SqlTimestampConverter;
58  import org.seasar.cubby.plugins.guice.spi.GuiceContainerProvider;
59  import org.seasar.cubby.plugins.guice.spi.GuiceConverterProvider;
60  import org.seasar.cubby.plugins.guice.spi.GuicePathResolverProvider;
61  import org.seasar.cubby.plugins.guice.spi.GuiceRequestParserProvider;
62  import org.seasar.cubby.routing.PathResolver;
63  import org.seasar.cubby.routing.PathTemplateParser;
64  import org.seasar.cubby.routing.impl.PathResolverImpl;
65  import org.seasar.cubby.routing.impl.PathTemplateParserImpl;
66  import org.seasar.cubby.spi.BeanDescProvider;
67  import org.seasar.cubby.spi.ContainerProvider;
68  import org.seasar.cubby.spi.ConverterProvider;
69  import org.seasar.cubby.spi.PathResolverProvider;
70  import org.seasar.cubby.spi.RequestParserProvider;
71  import org.seasar.cubby.spi.beans.impl.DefaultBeanDescProvider;
72  import org.seasar.cubby.util.ActionUtils;
73  
74  import com.google.inject.AbstractModule;
75  import com.google.inject.Injector;
76  import com.google.inject.Key;
77  import com.google.inject.Module;
78  import com.google.inject.Provides;
79  import com.google.inject.Singleton;
80  import com.google.inject.TypeLiteral;
81  import com.google.inject.matcher.AbstractMatcher;
82  import com.google.inject.servlet.RequestScoped;
83  import com.google.inject.spi.TypeEncounter;
84  import com.google.inject.spi.TypeListener;
85  
86  /**
87   * Cubby の設定を行う {@link Module} です。
88   * 
89   * @author baba
90   */
91  public class CubbyModule extends AbstractModule {
92  
93  	/** コンテナに登録されたアクションクラスのセット。 */
94  	private final Set<Class<?>> actionClasses = new LinkedHashSet<Class<?>>();
95  
96  	/** コンテナからコンバータを取得するための {@link Key} のセット。 */
97  	private final Set<Key<? extends Converter>> converterKeys = new LinkedHashSet<Key<? extends Converter>>();
98  
99  	/** コンテナから要求解析器を取得するための {@link Key} のセット。 */
100 	private final Set<Key<? extends RequestParser>> requestParserKeys = new LinkedHashSet<Key<? extends RequestParser>>();
101 
102 	/**
103 	 * Cubby を構成します。
104 	 */
105 	@Override
106 	public void configure() {
107 		configureTypeListeners();
108 		configureProviders();
109 		configureComponents();
110 		configureDefaultRequestParsers();
111 		configureDefaultConverters();
112 	}
113 
114 	/**
115 	 * {@link TypeListener} をバインドします。
116 	 */
117 	private void configureTypeListeners() {
118 		// action class listener
119 		bindListener(new AbstractMatcher<TypeLiteral<?>>() {
120 
121 			public boolean matches(final TypeLiteral<?> typeLiteral) {
122 				final boolean matches = ActionUtils.isActionClass(typeLiteral
123 						.getRawType());
124 				return matches;
125 			}
126 
127 		}, new TypeListener() {
128 
129 			public <I> void hear(final TypeLiteral<I> typeLiteral,
130 					final TypeEncounter<I> typeEncounter) {
131 				final Class<?> actionClass = typeLiteral.getRawType();
132 				actionClasses.add(actionClass);
133 			}
134 
135 		});
136 
137 		// converter listener
138 		bindListener(new AbstractMatcher<TypeLiteral<?>>() {
139 
140 			public boolean matches(final TypeLiteral<?> typeLiteral) {
141 				final boolean matches = Converter.class
142 						.isAssignableFrom(typeLiteral.getRawType());
143 				return matches;
144 			}
145 
146 		}, new TypeListener() {
147 
148 			public <I> void hear(final TypeLiteral<I> typeLiteral,
149 					final TypeEncounter<I> typeEncounter) {
150 				final Key<? extends Converter> key = key(typeLiteral);
151 				converterKeys.add(key);
152 			}
153 
154 		});
155 
156 		// request parser listener
157 		bindListener(new AbstractMatcher<TypeLiteral<?>>() {
158 
159 			public boolean matches(final TypeLiteral<?> typeLiteral) {
160 				final boolean matches = RequestParser.class
161 						.isAssignableFrom(typeLiteral.getRawType());
162 				return matches;
163 			}
164 
165 		}, new TypeListener() {
166 
167 			public <I> void hear(final TypeLiteral<I> typeLiteral,
168 					final TypeEncounter<I> typeEncounter) {
169 				final Key<? extends RequestParser> key = key(typeLiteral);
170 				requestParserKeys.add(key);
171 			}
172 
173 		});
174 	}
175 
176 	/**
177 	 * 以下のクラスをバインドします。
178 	 * <ul>
179 	 * <li>{@link ContainerProvider}</li>
180 	 * <li>{@link BeanDescProvider}</li>
181 	 * <li>{@link RequestParserProvider}</li>
182 	 * <li>{@link ConverterProvider}</li>
183 	 * <li>{@link PathResolverProvider}</li>
184 	 * </ul>
185 	 */
186 	private void configureProviders() {
187 		bind(ContainerProvider.class).to(GuiceContainerProvider.class)
188 				.asEagerSingleton();
189 		bind(BeanDescProvider.class).to(DefaultBeanDescProvider.class)
190 				.asEagerSingleton();
191 		bind(RequestParserProvider.class).to(GuiceRequestParserProvider.class)
192 				.asEagerSingleton();
193 		bind(ConverterProvider.class).to(GuiceConverterProvider.class)
194 				.asEagerSingleton();
195 		bind(PathResolverProvider.class).to(GuicePathResolverProvider.class)
196 				.asEagerSingleton();
197 	}
198 
199 	/**
200 	 * 以下のクラスをバインドします。
201 	 * <ul>
202 	 * <li>{@link MessagesBehaviour}</li>
203 	 * <li>{@link FormatPattern}</li>
204 	 * </ul>
205 	 */
206 	private void configureComponents() {
207 		bind(MessagesBehaviour.class).to(DefaultMessagesBehaviour.class)
208 				.asEagerSingleton();
209 		bind(FormatPattern.class).to(DefaultFormatPattern.class)
210 				.asEagerSingleton();
211 	}
212 
213 	/**
214 	 * 標準の要求解析器をコンテナに登録します。
215 	 * <p>
216 	 * 以下の要求解析器を登録します。
217 	 * <ol>
218 	 * <li>{@link MultipartRequestParser}</li>
219 	 * <li>{@link DefaultRequestParser}</li>
220 	 * </ol>
221 	 * </p>
222 	 * <p>
223 	 * 要求を解析する場合は、コンテナに登録された順序で
224 	 * {@link RequestParser#isParsable(javax.servlet.http.HttpServletRequest)}
225 	 * が評価されて、最初に <code>true</code> を返したインスタンスを解析に使用します。
226 	 * {@link DefaultRequestParser#isParsable(javax.servlet.http.HttpServletRequest)}
227 	 * は、常に <code>true</code> を返すので、 このメソッドをオーバーライドする場合は
228 	 * {@link DefaultRequestParser} のインスタンスを最後に登録するようにしてください。
229 	 * </p>
230 	 */
231 	protected void configureDefaultRequestParsers() {
232 		bind(MultipartRequestParser.class).asEagerSingleton();
233 		bind(DefaultRequestParser.class).asEagerSingleton();
234 	}
235 
236 	/**
237 	 * 標準のコンバータをコンテナに登録します。
238 	 * <p>
239 	 * 以下のコンバータが登録されます。
240 	 * <ul>
241 	 * <li>{@link BigDecimalConverter}</li>
242 	 * <li>{@link BigIntegerConverter}</li>
243 	 * <li>{@link BooleanConverter}</li>
244 	 * <li>{@link ByteArrayFileItemConverter}</li>
245 	 * <li>{@link ByteConverter}</li>
246 	 * <li>{@link CharacterConverter}</li>
247 	 * <li>{@link DateConverter}</li>
248 	 * <li>{@link DoubleConverter}</li>
249 	 * <li>{@link EnumConverter}</li>
250 	 * <li>{@link FloatConverter}</li>
251 	 * <li>{@link InputStreamFileItemConverter}</li>
252 	 * <li>{@link IntegerConverter}</li>
253 	 * <li>{@link LongConverter}</li>
254 	 * <li>{@link ShortConverter}</li>
255 	 * <li>{@link SqlDateConverter}</li>
256 	 * <li>{@link SqlTimeConverter}</li>
257 	 * <li>{@link SqlTimestampConverter}</li>
258 	 * </ul>
259 	 * </p>
260 	 */
261 	protected void configureDefaultConverters() {
262 		bind(BigDecimalConverter.class).asEagerSingleton();
263 		bind(BigIntegerConverter.class).asEagerSingleton();
264 		bind(BooleanConverter.class).asEagerSingleton();
265 		bind(ByteArrayFileItemConverter.class).asEagerSingleton();
266 		bind(ByteConverter.class).asEagerSingleton();
267 		bind(CharacterConverter.class).asEagerSingleton();
268 		bind(DateConverter.class).asEagerSingleton();
269 		bind(DoubleConverter.class).asEagerSingleton();
270 		bind(EnumConverter.class).asEagerSingleton();
271 		bind(FloatConverter.class).asEagerSingleton();
272 		bind(InputStreamFileItemConverter.class).asEagerSingleton();
273 		bind(IntegerConverter.class).asEagerSingleton();
274 		bind(LongConverter.class).asEagerSingleton();
275 		bind(ShortConverter.class).asEagerSingleton();
276 		bind(SqlDateConverter.class).asEagerSingleton();
277 		bind(SqlTimeConverter.class).asEagerSingleton();
278 		bind(SqlTimestampConverter.class).asEagerSingleton();
279 	}
280 
281 	// Provider methods ------------------------------------------------
282 
283 	@Provides
284 	@Singleton
285 	Collection<RequestParser> provideRequestParsers(final Injector injector) {
286 		final Set<RequestParser> requestParsers = new LinkedHashSet<RequestParser>();
287 		for (final Key<? extends RequestParser> key : requestParserKeys) {
288 			final RequestParser requestParser = injector.getInstance(key);
289 			requestParsers.add(requestParser);
290 		}
291 		return Collections.unmodifiableCollection(requestParsers);
292 	}
293 
294 	@Provides
295 	@Singleton
296 	Collection<Converter> provideConverters(final Injector injector) {
297 		final Set<Converter> converters = new HashSet<Converter>();
298 		for (final Key<? extends Converter> key : converterKeys) {
299 			final Converter converter = injector.getInstance(key);
300 			converters.add(converter);
301 		}
302 		return Collections.unmodifiableCollection(converters);
303 	}
304 
305 	@Provides
306 	@Singleton
307 	PathTemplateParser providePathTemplateParser() {
308 		final PathTemplateParser pathTemplateParser = new PathTemplateParserImpl();
309 		return pathTemplateParser;
310 	}
311 
312 	@Provides
313 	@Singleton
314 	PathResolver providePathResolver(final PathTemplateParser pathTemplateParser) {
315 		final PathResolver pathResolver = new PathResolverImpl(
316 				pathTemplateParser);
317 		for (final Class<?> actionClass : actionClasses) {
318 			pathResolver.add(actionClass);
319 		}
320 		return pathResolver;
321 	}
322 
323 	@Provides
324 	@RequestScoped
325 	ActionErrors provideActionErrors(final HttpServletRequest request) {
326 		final ActionErrors actionErrors = new ActionErrorsProxy(request);
327 		return actionErrors;
328 	}
329 
330 	@Provides
331 	@RequestScoped
332 	FlashMap provideFlashMap(final HttpServletRequest request) {
333 		final FlashMap flashMap = new FlashMapProxy(request);
334 		return flashMap;
335 	}
336 
337 	@Provides
338 	@RequestScoped
339 	ActionContext provideActionContext(final HttpServletRequest request) {
340 		final ActionContext actionContext = new ActionContextProxy(request);
341 		return actionContext;
342 	}
343 
344 	private static <T> Key<T> key(final TypeLiteral<?> typeLiteral) {
345 		@SuppressWarnings("unchecked")
346 		final Key<T> key = (Key<T>) Key.get(typeLiteral);
347 		return key;
348 	}
349 
350 }