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 }