View Javadoc

1   // Copyright 2008 Thiago H. de Paula Figueiredo
2   //
3   // Licensed under the Apache License, Version 2.0 (the "License");
4   // you may not use this file except in compliance with the License.
5   // You may obtain a copy of the License at
6   //
7   //     http://www.apache.org/licenses/LICENSE-2.0
8   //
9   // Unless required by applicable law or agreed to in writing, software
10  // distributed under the License is distributed on an "AS IS" BASIS,
11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  // See the License for the specific language governing permissions and
13  // limitations under the License.
14  
15  package br.com.arsmachina.tapestrycrud.ioc;
16  
17  import static br.com.arsmachina.module.ioc.ApplicationModuleModule.getServiceIfExists;
18  
19  import java.util.Collection;
20  import java.util.HashSet;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.apache.tapestry5.PrimaryKeyEncoder;
26  import org.apache.tapestry5.SelectModel;
27  import org.apache.tapestry5.internal.InternalConstants;
28  import org.apache.tapestry5.ioc.Configuration;
29  import org.apache.tapestry5.ioc.MappedConfiguration;
30  import org.apache.tapestry5.ioc.ObjectLocator;
31  import org.apache.tapestry5.ioc.annotations.Inject;
32  import org.apache.tapestry5.ioc.annotations.Symbol;
33  import org.apache.tapestry5.ioc.services.ChainBuilder;
34  import org.apache.tapestry5.ioc.services.ClassNameLocator;
35  import org.apache.tapestry5.ioc.services.TypeCoercer;
36  import org.apache.tapestry5.services.LibraryMapping;
37  import org.apache.tapestry5.services.ValueEncoderFactory;
38  import org.apache.tapestry5.services.ValueEncoderSource;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  import br.com.arsmachina.controller.Controller;
43  import br.com.arsmachina.module.DefaultModule;
44  import br.com.arsmachina.module.Module;
45  import br.com.arsmachina.module.ioc.ApplicationModuleModule;
46  import br.com.arsmachina.module.service.ControllerSource;
47  import br.com.arsmachina.module.service.EntitySource;
48  import br.com.arsmachina.module.service.ModuleService;
49  import br.com.arsmachina.tapestrycrud.encoder.ActivationContextEncoder;
50  import br.com.arsmachina.tapestrycrud.encoder.Encoder;
51  import br.com.arsmachina.tapestrycrud.encoder.LabelEncoder;
52  import br.com.arsmachina.tapestrycrud.factory.PrimaryKeyEncoderFactory;
53  import br.com.arsmachina.tapestrycrud.module.DefaultTapestryCrudModule;
54  import br.com.arsmachina.tapestrycrud.module.TapestryCrudModule;
55  import br.com.arsmachina.tapestrycrud.selectmodel.DefaultSingleTypeSelectModelFactory;
56  import br.com.arsmachina.tapestrycrud.selectmodel.SelectModelFactory;
57  import br.com.arsmachina.tapestrycrud.selectmodel.SingleTypeSelectModelFactory;
58  import br.com.arsmachina.tapestrycrud.selectmodel.impl.SelectModelFactoryImpl;
59  import br.com.arsmachina.tapestrycrud.services.ActivationContextEncoderSource;
60  import br.com.arsmachina.tapestrycrud.services.EncoderSource;
61  import br.com.arsmachina.tapestrycrud.services.LabelEncoderSource;
62  import br.com.arsmachina.tapestrycrud.services.PrimaryKeyEncoderSource;
63  import br.com.arsmachina.tapestrycrud.services.PrimaryKeyTypeService;
64  import br.com.arsmachina.tapestrycrud.services.TapestryCrudModuleService;
65  import br.com.arsmachina.tapestrycrud.services.impl.ActivationContextEncoderSourceImpl;
66  import br.com.arsmachina.tapestrycrud.services.impl.EncoderSourceImpl;
67  import br.com.arsmachina.tapestrycrud.services.impl.LabelEncoderSourceImpl;
68  import br.com.arsmachina.tapestrycrud.services.impl.PrimaryKeyEncoderSourceImpl;
69  import br.com.arsmachina.tapestrycrud.services.impl.PrimaryKeyEncoderValueEncoder;
70  import br.com.arsmachina.tapestrycrud.services.impl.TapestryCrudModuleServiceImpl;
71  
72  /**
73   * Tapestry-IoC module for Tapestry CRUD.
74   * 
75   * @author Thiago H. de Paula Figueiredo
76   */
77  public class TapestryCrudIoCModule {
78  
79  	/**
80  	 * Tapestry CRUD component prefix.
81  	 */
82  	final public static String TAPESTRY_CRUD_COMPONENT_PREFIX = "crud";
83  
84  	/**
85  	 * Tapestry CRUD version.
86  	 */
87  	final public static String TAPESTRY_CRUD_VERSION = "1.0";
88  
89  	/**
90  	 * Path under with the Tapestry CRUDs assets will be accessed.
91  	 */
92  	final public static String TAPESTRY_CRUD_ASSET_PREFIX = "tapestry-crud/"
93  			+ TAPESTRY_CRUD_VERSION;
94  
95  	final private static Logger LOGGER = LoggerFactory.getLogger(TapestryCrudIoCModule.class);
96  
97  	/**
98  	 * Builds the {@link ModuleService} service.
99  	 * 
100 	 * @param contributions a {@link Map<Class, Controller>}.
101 	 * @return an {@link ControllerSource}.
102 	 */
103 	@SuppressWarnings("unchecked")
104 	public static void contributeTapestryCrudModuleService(
105 			Configuration<TapestryCrudModule> contributions, ModuleService moduleService,
106 			ClassNameLocator classNameLocator,
107 			@Inject
108 			@Symbol(ApplicationModuleModule.DAO_IMPLEMENTATION_SUBPACKAGE_SYMBOL)
109 			String daoImplementationSubpackage) {
110 
111 		final Set<Module> modules = moduleService.getModules();
112 
113 		for (Module module : modules) {
114 
115 			final String name = module.getName();
116 			final String rootPackage = module.getRootPackage();
117 			final DefaultTapestryCrudModule dtcModule = new DefaultTapestryCrudModule(name,
118 					rootPackage, classNameLocator, daoImplementationSubpackage);
119 			contributions.add(dtcModule);
120 		}
121 
122 	}
123 
124 	/**
125 	 * Builds the {@link ModuleService} service.
126 	 * 
127 	 * @param contributions a {@link Map<Class, Controller>}.
128 	 * @return an {@link ControllerSource}.
129 	 */
130 	@SuppressWarnings("unchecked")
131 	public static TapestryCrudModuleService buildTapestryCrudModuleService(
132 			Collection<TapestryCrudModule> contributions) {
133 
134 		return new TapestryCrudModuleServiceImpl(new HashSet(contributions));
135 
136 	}
137 
138 	/**
139 	 * Contributes all ({@link Class}, {@link Encoder} pairs registered in {@link EncoderSource}
140 	 * to {@link ValueEncoderSource}. If no {@link Encoder} is found for a given entity class, if a
141 	 * {@link PrimaryKeyEncoder} is found, a {@link ValueEncoderFactory} is automatically created
142 	 * using the {@link PrimaryKeyEncoder}.
143 	 * 
144 	 * @param configuration
145 	 * @param encoderSource
146 	 */
147 	@SuppressWarnings("unchecked")
148 	public static void contributeValueEncoderSource(
149 			MappedConfiguration<Class, ValueEncoderFactory> configuration,
150 			EncoderSource encoderSource, EntitySource entitySource,
151 			PrimaryKeyEncoderSource primaryKeyEncoderSource,
152 			PrimaryKeyTypeService primaryKeyTypeService, TypeCoercer typeCoercer) {
153 
154 		Set<Class<?>> classes = entitySource.getEntityClasses();
155 
156 		for (Class clasz : classes) {
157 
158 			final Encoder encoder = encoderSource.get(clasz);
159 
160 			if (encoder != null) {
161 				configuration.add(clasz, encoder);
162 			}
163 			else {
164 
165 				PrimaryKeyEncoder primaryKeyEncoder = primaryKeyEncoderSource.get(clasz);
166 
167 				if (primaryKeyEncoder != null) {
168 
169 					final Class primaryKeyType = primaryKeyTypeService.getPrimaryKeyType(clasz);
170 					ValueEncoderFactory valueEncoderFactory = new PrimaryKeyEncoderValueEncoder(
171 							primaryKeyType, primaryKeyEncoder, typeCoercer);
172 
173 					configuration.add(clasz, valueEncoderFactory);
174 
175 				}
176 
177 			}
178 
179 		}
180 
181 	}
182 
183 	/**
184 	 * Builds the {@link ActivationContextEncoderSource} service.
185 	 * 
186 	 * @param contributions a {@link Map<Class, ActivationContextEncoder>}.
187 	 * @return an {@link ActivationContextEncoderSource}.
188 	 */
189 	@SuppressWarnings("unchecked")
190 	public static ActivationContextEncoderSource buildActivationContextEncoderSource(
191 			Map<Class, ActivationContextEncoder> contributions, EncoderSource encoderSource,
192 			PrimaryKeyEncoderSource primaryKeyEncoderSource,
193 			PrimaryKeyTypeService primaryKeyTypeService) {
194 
195 		return new ActivationContextEncoderSourceImpl(contributions, encoderSource,
196 				primaryKeyEncoderSource, primaryKeyTypeService);
197 
198 	}
199 
200 	/**
201 	 * Builds the {@link LabelSource} service.
202 	 * 
203 	 * @param contributions a {@link Map<Class, ActivationContextEncoder>}.
204 	 * @return an {@link ActivationContextEncoderSource}.
205 	 */
206 	@SuppressWarnings("unchecked")
207 	public static LabelEncoderSource buildLabelEncoderSource(
208 			Map<Class, LabelEncoder> contributions, EncoderSource encoderSource) {
209 		return new LabelEncoderSourceImpl(contributions, encoderSource);
210 	}
211 
212 	/**
213 	 * Builds the {@link PrimaryKeyEncoderSource} service.
214 	 * 
215 	 * @param contributions a {@link Map<Class, PrimaryKeyEncoder>}.
216 	 * @param encoderSource an {@link EncoderSource}.
217 	 * @param primaryKeyEncoderFactory a {@link PrimaryKeyEncoderFactory}.
218 	 * @return an {@link PrimaryKeyEncoderSource}.
219 	 */
220 	@SuppressWarnings("unchecked")
221 	public static PrimaryKeyEncoderSource buildPrimaryKeyEncoderSource(
222 			Map<Class, PrimaryKeyEncoder> contributions, EncoderSource encoderSource,
223 			PrimaryKeyEncoderFactory primaryKeyEncoderFactory) {
224 		
225 		return new PrimaryKeyEncoderSourceImpl(contributions, encoderSource,
226 				primaryKeyEncoderFactory);
227 		
228 	}
229 
230 	/**
231 	 * Builds the {@link EncoderSource} service.
232 	 * 
233 	 * @param contributions a {@link Map<Class, Encoder>}.
234 	 * @return an {@link EncoderSource}.
235 	 */
236 	@SuppressWarnings("unchecked")
237 	public static EncoderSource buildEncoderSource(Map<Class, Encoder> contributions) {
238 		return new EncoderSourceImpl(contributions);
239 	}
240 
241 	/**
242 	 * Builds the {@link SelectModelFactory} service.
243 	 * 
244 	 * @param contributions a {@link Map<Class, SingleTypeSelectModelFactory>}.
245 	 * @return a {@link SelectModelFactory}.
246 	 */
247 	@SuppressWarnings("unchecked")
248 	public static SelectModelFactory buildSelectModelFactory(
249 			Map<Class, SingleTypeSelectModelFactory> contributions) {
250 		return new SelectModelFactoryImpl(contributions);
251 	}
252 
253 	/**
254 	 * Contributes the Tapestry CRUD components under the <code>crud</code> prefix.
255 	 * 
256 	 * @param configuration a {@link Configuration}.
257 	 */
258 	public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration) {
259 
260 		configuration.add(new LibraryMapping(TAPESTRY_CRUD_COMPONENT_PREFIX,
261 				"br.com.arsmachina.tapestrycrud"));
262 
263 	}
264 
265 	public static void contributeClasspathAssetAliasManager(
266 			MappedConfiguration<String, String> configuration) {
267 		configuration.add(TAPESTRY_CRUD_ASSET_PREFIX, "br/com/arsmachina/tapestrycrud/components");
268 	}
269 
270 	/**
271 	 * Contributes the main (default module) to the {@link ModuleService} service.
272 	 * 
273 	 * @param configuration a {@link Configuration} of {@link Module}s.
274 	 */
275 	public static void contributeModuleService(Configuration<Module> configuration,
276 			ClassNameLocator classNameLocator, 
277 			@Inject
278 			@Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
279 			final String tapestryRootPackage,
280 			@Inject
281 			@Symbol(ApplicationModuleModule.DAO_IMPLEMENTATION_SUBPACKAGE_SYMBOL)
282 			String daoImplementationSubpackage) {
283 
284 		// The convention is that the module root is one package level above the
285 		// Tapestry root package
286 
287 		String modulePackage = tapestryRootPackage.substring(0,
288 				tapestryRootPackage.lastIndexOf('.'));
289 
290 		final DefaultModule module = new DefaultModule("Root", modulePackage, classNameLocator, 
291 				daoImplementationSubpackage);
292 		configuration.add(module);
293 
294 		final DefaultModule module2 = new DefaultModule("Root 2", "sdfasdfas", classNameLocator, 
295 				daoImplementationSubpackage);
296 		configuration.add(module2);
297 
298 	}
299 
300 	/**
301 	 * Associates entity classes with their {@link SelectModel}s.
302 	 * 
303 	 * @param contributions a {@link MappedConfiguration}.
304 	 */
305 	@SuppressWarnings("unchecked")
306 	public static void contributeSelectModelFactory(
307 			MappedConfiguration<Class, SingleTypeSelectModelFactory> contributions,
308 			ControllerSource controllerSource, EntitySource entitySource,
309 			LabelEncoderSource labelEncoderSource) {
310 
311 		final Set<Class<?>> entityClasses = entitySource.getEntityClasses();
312 
313 		for (Class<?> entityClass : entityClasses) {
314 
315 			Controller controller = controllerSource.get(entityClass);
316 			LabelEncoder labelEncoder = labelEncoderSource.get(entityClass);
317 
318 			if (controller != null && labelEncoder != null) {
319 
320 				SingleTypeSelectModelFactory stsmf = new DefaultSingleTypeSelectModelFactory(
321 						controller, labelEncoder);
322 
323 				contributions.add(entityClass, stsmf);
324 
325 			}
326 
327 		}
328 
329 	}
330 
331 	/**
332 	 * Associates entity classes with their {@link Encoder}s.
333 	 * 
334 	 * @param contributions a {@link MappedConfiguration}.
335 	 */
336 	@SuppressWarnings("unchecked")
337 	public static void contributeEncoderSource(MappedConfiguration<Class, Encoder> contributions,
338 			EntitySource entitySource, ModuleService moduleService,
339 			TapestryCrudModuleService tapestryCrudModuleService, ObjectLocator objectLocator) {
340 
341 		final Set<Class<?>> entityClasses = entitySource.getEntityClasses();
342 		Encoder encoder = null;
343 
344 		for (Class<?> entityClass : entityClasses) {
345 
346 			final Class<?> encoderClass = tapestryCrudModuleService.getEncoderClass(entityClass);
347 
348 			if (encoderClass != null) {
349 
350 				encoder = (Encoder) getServiceIfExists(encoderClass, objectLocator);
351 
352 				if (encoder == null) {
353 					encoder = (Encoder) objectLocator.autobuild(encoderClass);
354 				}
355 
356 				contributions.add(entityClass, encoder);
357 
358 				if (LOGGER.isInfoEnabled()) {
359 
360 					final String entityName = entityClass.getSimpleName();
361 					final String encoderClassName = encoder.getClass().getName();
362 					final String message = String.format("Associating entity %s with encoder %s",
363 							entityName, encoderClassName);
364 
365 					LOGGER.info(message);
366 
367 				}
368 
369 			}
370 
371 		}
372 
373 	}
374 
375 	/**
376 	 * Associates entity classes with their {@link Controller}s.
377 	 * 
378 	 * @param contributions a {@link MappedConfiguration}.
379 	 */
380 	@SuppressWarnings("unchecked")
381 	public static void contributeActivationContextEncoderSource(
382 			MappedConfiguration<Class, ActivationContextEncoder> contributions,
383 			EntitySource entitySource, ModuleService moduleService,
384 			TapestryCrudModuleService tapestryCrudModuleService, ObjectLocator objectLocator) {
385 
386 		final Set<Class<?>> entityClasses = entitySource.getEntityClasses();
387 		ActivationContextEncoder encoder = null;
388 
389 		for (Class<?> entityClass : entityClasses) {
390 
391 			final Class<?> encoderClass = tapestryCrudModuleService.getActivationContextEncoderClass(entityClass);
392 
393 			// If the entity class has no activation context encoder, we don't register
394 			// a one for it for it.
395 			if (encoderClass != null) {
396 
397 				encoder = (ActivationContextEncoder) getServiceIfExists(encoderClass, objectLocator);
398 
399 				if (encoder == null) {
400 					encoder = (ActivationContextEncoder) objectLocator.autobuild(encoderClass);
401 				}
402 
403 				contributions.add(entityClass, encoder);
404 
405 				if (LOGGER.isInfoEnabled()) {
406 
407 					final String entityName = entityClass.getSimpleName();
408 					final String encoderClassName = encoder.getClass().getName();
409 					final String message = String.format(
410 							"Associating entity %s with activation context encoder %s", entityName,
411 							encoderClassName);
412 
413 					LOGGER.info(message);
414 
415 				}
416 
417 			}
418 
419 		}
420 
421 	}
422 
423 	/**
424 	 * Associates entity classes with their {@link LabelEncoder}s.
425 	 * 
426 	 * @param contributions a {@link MappedConfiguration}.
427 	 */
428 	@SuppressWarnings("unchecked")
429 	public static void contributeLabelEncoderSource(
430 			MappedConfiguration<Class, LabelEncoder> contributions, EntitySource entitySource,
431 			ModuleService moduleService, TapestryCrudModuleService tapestryCrudModuleService,
432 			ObjectLocator objectLocator) {
433 
434 		final Set<Class<?>> entityClasses = entitySource.getEntityClasses();
435 		LabelEncoder encoder = null;
436 
437 		for (Class<?> entityClass : entityClasses) {
438 
439 			final Class<?> encoderClass = tapestryCrudModuleService.getLabelEncoderClass(entityClass);
440 
441 			// If the entity class has no activation context encoder, we don't register
442 			// a one for it for it.
443 			if (encoderClass != null) {
444 
445 				encoder = (LabelEncoder) getServiceIfExists(encoderClass, objectLocator);
446 
447 				if (encoder == null) {
448 					encoder = (LabelEncoder) objectLocator.autobuild(encoderClass);
449 				}
450 
451 				contributions.add(entityClass, encoder);
452 
453 				if (LOGGER.isInfoEnabled()) {
454 
455 					final String entityName = entityClass.getSimpleName();
456 					final String encoderClassName = encoder.getClass().getName();
457 					final String message = String.format(
458 							"Associating entity %s with label encoder %s", entityName,
459 							encoderClassName);
460 
461 					LOGGER.info(message);
462 
463 				}
464 
465 			}
466 
467 		}
468 
469 	}
470 
471 	/**
472 	 * Associates entity classes with their {@link PrimaryKeyEncoder}s.
473 	 * 
474 	 * @param contributions a {@link MappedConfiguration}.
475 	 */
476 	@SuppressWarnings("unchecked")
477 	public static void contributePrimaryKeyEncoderSource(
478 			MappedConfiguration<Class, PrimaryKeyEncoder> contributions, EntitySource entitySource,
479 			ModuleService moduleService, TapestryCrudModuleService tapestryCrudModuleService,
480 			ObjectLocator objectLocator) {
481 
482 		final Set<Class<?>> entityClasses = entitySource.getEntityClasses();
483 		PrimaryKeyEncoder encoder = null;
484 
485 		for (Class<?> entityClass : entityClasses) {
486 
487 			final Class<?> encoderClass = tapestryCrudModuleService.getPrimaryKeyEncoderClass(entityClass);
488 
489 			// If the entity class has no primary key encoder, we don't register
490 			// a one for it for it.
491 			if (encoderClass != null) {
492 
493 				encoder = (PrimaryKeyEncoder) getServiceIfExists(encoderClass, objectLocator);
494 
495 				if (encoder == null) {
496 					encoder = (PrimaryKeyEncoder) objectLocator.autobuild(encoderClass);
497 				}
498 
499 				contributions.add(entityClass, encoder);
500 
501 				if (LOGGER.isInfoEnabled()) {
502 
503 					final String entityName = entityClass.getSimpleName();
504 					final String encoderClassName = encoder.getClass().getName();
505 					final String message = String.format(
506 							"Associating entity %s with primary key encoder %s", entityName,
507 							encoderClassName);
508 
509 					LOGGER.info(message);
510 
511 				}
512 
513 			}
514 
515 		}
516 
517 	}
518 
519 	/**
520 	 * Builds the {@link PrimaryKeyEncoderFactory} service.
521 	 * 
522 	 * @param contributions a {@link List} of {@link PrimaryKeyEncoderFactory}.
523 	 * @param chainBuilder a {@link ChainBuilder}.
524 	 * @return a {@link DAOFactory}.
525 	 */
526 	public PrimaryKeyEncoderFactory buildPrimaryKeyEncoderFactory(
527 			final List<PrimaryKeyEncoderFactory> contributions, ChainBuilder chainBuilder) {
528 
529 		return chainBuilder.build(PrimaryKeyEncoderFactory.class, contributions);
530 
531 	}
532 
533 	/**
534 	 * Builds the {@link DefaultLabelEncoderFactory} service.
535 	 * 
536 	 * @param contributions a {@link List} of {@link PrimaryKeyTypeService}.
537 	 * @param chainBuilder a {@link ChainBuilder}.
538 	 * @return a {@link PrimaryKeyTypeService}.
539 	 */
540 	public PrimaryKeyTypeService buildPrimaryKeyTypeService(
541 			final List<PrimaryKeyTypeService> contributions, ChainBuilder chainBuilder) {
542 
543 		return chainBuilder.build(PrimaryKeyTypeService.class, contributions);
544 
545 	}
546 
547 }