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