package org.tynamo.services; import org.apache.tapestry5.alerts.AlertManager; import org.apache.tapestry5.annotations.Path; import org.apache.tapestry5.beaneditor.DataTypeConstants; import org.apache.tapestry5.func.Predicate; import org.apache.tapestry5.grid.GridDataSource; import org.apache.tapestry5.internal.services.PageResponseRenderer; import org.apache.tapestry5.internal.structure.Page; import org.apache.tapestry5.ioc.*; import org.apache.tapestry5.ioc.annotations.Autobuild; import org.apache.tapestry5.ioc.annotations.Contribute; import org.apache.tapestry5.ioc.annotations.InjectService; import org.apache.tapestry5.ioc.annotations.Marker; import org.apache.tapestry5.ioc.annotations.Match; import org.apache.tapestry5.ioc.annotations.Primary; import org.apache.tapestry5.ioc.services.ChainBuilder; import org.apache.tapestry5.ioc.services.CoercionTuple; import org.apache.tapestry5.ioc.services.PropertyAccess; import org.apache.tapestry5.plastic.*; import org.apache.tapestry5.services.*; import org.apache.tapestry5.services.javascript.JavaScriptModuleConfiguration; import org.apache.tapestry5.services.javascript.ModuleManager; import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2; import org.slf4j.Logger; import org.tynamo.bindings.ModelBindingFactory; import org.tynamo.blob.BlobManager; import org.tynamo.blob.IconResolver; import org.tynamo.builder.BuilderDirector; import org.tynamo.common.ModuleProperties; import org.tynamo.descriptor.TynamoClassDescriptor; import org.tynamo.descriptor.TynamoPropertyDescriptor; import org.tynamo.descriptor.annotation.handlers.*; import org.tynamo.descriptor.decorators.DescriptorDecorator; import org.tynamo.descriptor.decorators.TapestryDecorator; import org.tynamo.descriptor.decorators.TynamoDecorator; import org.tynamo.descriptor.factories.*; import org.tynamo.internal.services.BeanModelExtensionBMModifier; import org.tynamo.internal.services.BeanModelsAnnotationBMModifier; import org.tynamo.internal.services.BeanModelSourceAdvice; import org.tynamo.internal.services.BeanModelSourceAdviceImpl; import org.tynamo.internal.services.BeanModelWorker; import org.tynamo.internal.services.DefaultExclusionsBMModifier; import org.tynamo.search.SearchFilterPredicate; import org.tynamo.util.Pair; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.Set; public class TynamoCoreModule { private static final String version = ModuleProperties.getVersion(TynamoCoreModule.class); public final static String PROPERTY_DISPLAY_BLOCKS = "tynamo/PropertyDisplayBlocks"; public final static String PROPERTY_EDIT_BLOCKS = "tynamo/PropertyEditBlocks"; public static void bind(ServiceBinder binder) { // Make bind() calls on the binder object to define most IoC services. // Use service builder methods (example below) when the implementation // is provided inline, or requires more initialization than simply // invoking the constructor. binder.bind(BuilderDirector.class, BuilderDirector.class); binder.bind(DescriptorFactory.class, ReflectionDescriptorFactory.class); binder.bind(PropertyDescriptorFactory.class, PropertyDescriptorFactoryImpl.class); binder.bind(MethodDescriptorFactory.class, MethodDescriptorFactoryImpl.class); binder.bind(EntityCoercerService.class, EntityCoercerServiceImpl.class); binder.bind(DescriptorService.class, DescriptorServiceImpl.class); binder.bind(TynamoDataTypeAnalyzer.class, TynamoDataTypeAnalyzer.class); binder.bind(SearchFilterBlockSource.class); binder.bind(SearchFilterBlockOverrideSource.class); binder.bind(BlobManager.class).withId("DefaultBlobManager"); binder.bind(DescriptorAnnotationHandler.class, BeanModelAnnotationHandler.class).withId("BeanModelAnnotationHandler"); binder.bind(DescriptorAnnotationHandler.class, BlobDescriptorAnnotationHandler.class).withId("BlobDescriptorAnnotationHandler"); binder.bind(DescriptorAnnotationHandler.class, ClassDescriptorAnnotationHandler.class).withId("ClassDescriptorAnnotationHandler"); binder.bind(DescriptorAnnotationHandler.class, CollectionDescriptorAnnotationHandler.class).withId("CollectionDescriptorAnnotationHandler"); binder.bind(DescriptorAnnotationHandler.class, MethodDescriptorAnnotationHandler.class).withId("MethodDescriptorAnnotationHandler"); binder.bind(DescriptorAnnotationHandler.class, PropertyDescriptorAnnotationHandler.class).withId("PropertyDescriptorAnnotationHandler"); binder.bind(BeanModelSourceAdvice.class, BeanModelSourceAdviceImpl.class); binder.bind(BeanModelsAnnotationBMModifier.class); binder.bind(BeanModelWorker.class); binder.bind(IconResolver.class); } @Match("BeanModelSource") public static void adviseBeanModelSource(MethodAdviceReceiver receiver, BeanModelSourceAdvice advice) { receiver.adviseAllMethods(advice); } /** * Add our components and pages to the "Tynamo" library. */ public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration) { configuration.add(new LibraryMapping("tynamo", "org.tynamo")); } public static void contributeClasspathAssetAliasManager(MappedConfiguration<String, String> configuration) { configuration.add("tynamo-" + version, "org/tynamo"); } /** * Contribution to the BeanBlockSource service to tell the BeanEditForm component about the editors. */ public static void contributeBeanBlockSource(Configuration<BeanBlockContribution> configuration) { configuration.add(new EditBlockContribution("nonVisual", PROPERTY_EDIT_BLOCKS, "nonVisual")); configuration.add(new EditBlockContribution("formatted-date", "PropertyEditBlocks", DataTypeConstants.DATE)); configuration.add(new EditBlockContribution("formatted-number", "PropertyEditBlocks", DataTypeConstants.NUMBER)); configuration.add(new EditBlockContribution("ckeditor", PROPERTY_EDIT_BLOCKS, "ckeditor")); configuration.add(new EditBlockContribution("readOnly", PROPERTY_EDIT_BLOCKS, "readOnly")); configuration.add(new EditBlockContribution("single-valued-association", PROPERTY_EDIT_BLOCKS, "select")); configuration.add(new EditBlockContribution("identifierEditor", PROPERTY_EDIT_BLOCKS, "identifierEditor")); configuration.add(new EditBlockContribution("many-valued-association", PROPERTY_EDIT_BLOCKS, "palette")); configuration.add(new EditBlockContribution("composition", PROPERTY_EDIT_BLOCKS, "editComposition")); configuration.add(new EditBlockContribution("embedded", PROPERTY_EDIT_BLOCKS, "embedded")); configuration.add(new EditBlockContribution("blob", PROPERTY_EDIT_BLOCKS, "blob")); configuration.add(new DisplayBlockContribution("nonVisual", PROPERTY_DISPLAY_BLOCKS, "nonVisual")); configuration.add(new DisplayBlockContribution("formatted-date", PROPERTY_DISPLAY_BLOCKS, "date")); configuration.add(new DisplayBlockContribution("formatted-number", PROPERTY_DISPLAY_BLOCKS, "number")); configuration.add(new DisplayBlockContribution("embedded", PROPERTY_DISPLAY_BLOCKS, "embedded")); configuration.add(new DisplayBlockContribution("composition", PROPERTY_DISPLAY_BLOCKS, "composition")); configuration.add(new DisplayBlockContribution("blob", PROPERTY_DISPLAY_BLOCKS, "download")); configuration.add(new DisplayBlockContribution("ckeditor", PROPERTY_DISPLAY_BLOCKS, "ckeditor")); } public static void contributeBeanBlockOverrideSource(Configuration<BeanBlockContribution> configuration) { configuration.add(new EditBlockContribution("enum", PROPERTY_EDIT_BLOCKS, "select")); } @Contribute(SearchFilterBlockSource.class) public static void provideDefaultSearchFilterBlocks(Configuration<SearchFilterBlockContribution> configuration) { configuration.add(new SearchFilterBlockContribution(DataTypeConstants.TEXT, "tynamo/PropertySearchFilterBlocks", DataTypeConstants.TEXT)); configuration.add(new SearchFilterBlockContribution("formatted-number", "tynamo/PropertySearchFilterBlocks", DataTypeConstants.NUMBER)); configuration.add(new SearchFilterBlockContribution(DataTypeConstants.NUMBER, "tynamo/PropertySearchFilterBlocks", DataTypeConstants.NUMBER)); configuration.add(new SearchFilterBlockContribution(DataTypeConstants.ENUM, "tynamo/PropertySearchFilterBlocks", DataTypeConstants.ENUM)); configuration.add(new SearchFilterBlockContribution(DataTypeConstants.BOOLEAN, "tynamo/PropertySearchFilterBlocks", DataTypeConstants.BOOLEAN)); configuration.add(new SearchFilterBlockContribution(DataTypeConstants.DATE, "tynamo/PropertySearchFilterBlocks", DataTypeConstants.DATE)); } @Primary @Contribute(DataTypeAnalyzer.class) public static void addTynamoDataTypeAnalyzer(OrderedConfiguration<DataTypeAnalyzer> configuration, TynamoDataTypeAnalyzer tynamoDataTypeAnalyzer) { configuration.add("Tynamo", tynamoDataTypeAnalyzer, "before:Default"); } /** * Contributes a set of standard type coercions to the {@link org.apache.tapestry5.ioc.services.TypeCoercer} service: * <ul> * <li>Class to String</li> * <li>String to Double</li> * </ul> */ @SuppressWarnings("unchecked") public static void contributeTypeCoercer(final Configuration<CoercionTuple> configuration, @InjectService("EntityCoercerService") EntityCoercerService entityCoercerService) { configuration.add(CoercionTuple.create(Class.class, String.class, new ClassToStringCoercion(entityCoercerService))); configuration.add(CoercionTuple.create(String.class, Class.class, new StringToClassCoercion(entityCoercerService))); } public static void contributeDescriptorFactory(OrderedConfiguration<DescriptorDecorator> configuration, PropertyAccess propertyAccess, ObjectLocator locator) { configuration.add("TynamoDecorator", new TynamoDecorator(locator)); configuration.add("TapestryDecorator", new TapestryDecorator(propertyAccess)); } @Contribute(TynamoDataTypeAnalyzer.class) public static void addTynamoDataTypeAnalyzerPredicates(OrderedConfiguration<Pair> configuration) { configuration.add("nonVisual", newPair(TynamoDataTypeAnalyzerPredicates.nonVisual, "nonVisual")); configuration.add("readOnly", newPair(TynamoDataTypeAnalyzerPredicates.readOnly, "readOnly")); configuration.add("generatedId", newPair(TynamoDataTypeAnalyzerPredicates.generatedId, "readOnly")); configuration.add("assignedId", newPair(TynamoDataTypeAnalyzerPredicates.assignedId, "identifierEditor")); configuration.add("richText", newPair(TynamoDataTypeAnalyzerPredicates.richText, "ckeditor")); configuration.add("password", newPair(TynamoDataTypeAnalyzerPredicates.password, "password")); configuration.add("date", newPair(TynamoDataTypeAnalyzerPredicates.date, "formatted-date")); configuration.add("number", newPair(TynamoDataTypeAnalyzerPredicates.number, "formatted-number")); configuration.add("longtext", newPair(TynamoDataTypeAnalyzerPredicates.longtext, "longtext")); // configuration.add("", newPair("identifier && objectReference", "objectReferenceIdentifierEditor"); configuration.add("enum", newPair(TynamoDataTypeAnalyzerPredicates.enumi, "enum")); // overrides Tapestry's enum configuration.add("blob", newPair(TynamoDataTypeAnalyzerPredicates.blob, "blob")); configuration.add("manyToOne", newPair(TynamoDataTypeAnalyzerPredicates.manyToOne, "single-valued-association")); configuration.add("manyToMany", newPair(TynamoDataTypeAnalyzerPredicates.manyToMany, "many-valued-association")); configuration.add("composition", newPair(TynamoDataTypeAnalyzerPredicates.composition, "composition")); configuration.add("embedded", newPair(TynamoDataTypeAnalyzerPredicates.embedded, "embedded")); } private static Pair<Predicate<TynamoPropertyDescriptor>, String> newPair(Predicate<TynamoPropertyDescriptor> predicate, String value) { return new Pair<Predicate<TynamoPropertyDescriptor>, String>(predicate, value); } public static void contributePropertyDescriptorFactory(Configuration<String> configuration) { configuration.add("exclude.*"); configuration.add("class"); } public static void contributeMethodDescriptorFactory(Configuration<String> configuration) { configuration.add("shouldExclude"); configuration.add("set.*"); configuration.add("get.*"); configuration.add("is.*"); configuration.add("equals"); configuration.add("wait"); configuration.add("toString"); configuration.add("notify.*"); configuration.add("hashCode"); } @Contribute(EntityCoercerService.class) public static void contributeEntityCoercerService(MappedConfiguration<String, Class> configuration, DescriptorService descriptorService) { for (TynamoClassDescriptor classDescriptor : descriptorService.getAllDescriptors()) { Class clazz = classDescriptor.getBeanType(); String simpleName = clazz.getSimpleName().toLowerCase(); configuration.add(simpleName, clazz); } } @Contribute(BindingSource.class) public void contributeBindingSource(MappedConfiguration<String, BindingFactory> configuration, @Autobuild ModelBindingFactory modelBindingFactory) { configuration.add("mb", modelBindingFactory); } public SearchableGridDataSourceProvider buildSearchableGridDataSourceProvider( final Logger logger, final AlertManager alertManager, final PersistenceService persistenceService) { // naive implementation to be overridden in persistence-specific sub modules return new SearchableGridDataSourceProvider() { @Override public GridDataSource createGridDataSource(Class entityType) { String message = "There is no search support configured, you are using a SearchableGridDataSourceProvider" + " naive implementation meant to be overridden in persistence-specific sub modules"; alertManager.error(message); logger.error(message); return persistenceService.getGridDataSource(entityType); } @Override public GridDataSource createGridDataSource(Class entityType, Map<TynamoPropertyDescriptor, SearchFilterPredicate> propertySearchFilterMap, List<TynamoPropertyDescriptor> searchablePropertyDescriptors, String searchTerm) { return createGridDataSource(entityType); } @Override public GridDataSource createGridDataSource(Class entityType, Set includedIds, Map<TynamoPropertyDescriptor, SearchFilterPredicate> propertySearchFilterMap) { return createGridDataSource(entityType); } }; } @Primary @Contribute(ComponentClassTransformWorker2.class) public static void provideTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2> configuration, BeanModelWorker beanModelWorker) { configuration.add("BeanModelWorker", beanModelWorker); } /** * Builds the {@link BeanModelModifier} service as a chain of command. */ @Marker(Primary.class) public BeanModelModifier buildBeanModelModifier(List<BeanModelModifier> configuration, ChainBuilder chainBuilder) { return chainBuilder.build(BeanModelModifier.class, configuration); } @Primary @Contribute(BeanModelModifier.class) public static void addBeanModelModifiers(OrderedConfiguration<BeanModelModifier> configuration, BeanModelsAnnotationBMModifier pageAnnotationsModifier, @Autobuild DefaultExclusionsBMModifier defaultModifier, @Autobuild BeanModelExtensionBMModifier extensionModifier) { configuration.add("pages", pageAnnotationsModifier); configuration.add("entities", extensionModifier); configuration.add("defaults", defaultModifier, "after:*"); } @Contribute(IconResolver.class) public static void addIcons(MappedConfiguration<String, String> configuration){ configuration.add("application/octet-stream", "org/tynamo/components/blob/image/asset/icgen.gif"); configuration.add("application/x-zip-compressed", "org/tynamo/components/blob/image/asset/winzip.gif"); configuration.add("application/pdf", "org/tynamo/components/blob/image/asset/icadobe.gif"); configuration.add("application/msword", "org/tynamo/components/blob/image/asset/icdoc.gif"); configuration.add("application/vnd.visio", "org/tynamo/components/blob/image/asset/icdoc.gif"); configuration.add("application/vnd.ms-powerpoint", "org/tynamo/components/blob/image/asset/icppt.gif"); configuration.add("application/vnd.ms-excel", "org/tynamo/components/blob/image/asset/icxls.gif"); configuration.add("text/html", "org/tynamo/components/blob/image/asset/ichtm.gif"); configuration.add("text/plain", "org/tynamo/components/blob/image/asset/ictxt.gif"); configuration.add("text/css", "org/tynamo/components/blob/image/asset/ictxt.gif"); configuration.add("text/xml", "org/tynamo/components/blob/image/asset/icxml.gif"); configuration.add("image/tiff", "org/tynamo/components/blob/image/asset/icgen.gif"); configuration.add("video/avi", "org/tynamo/components/blob/image/asset/icwmp.gif"); configuration.add("video/mpeg", "org/tynamo/components/blob/image/asset/icwmp.gif"); configuration.add("video/mp4", "org/tynamo/components/blob/image/asset/icwmp.gif"); configuration.add("video/quicktime", "org/tynamo/components/blob/image/asset/icwmp.gif"); configuration.add("video/x-ms-wmv", "org/tynamo/components/blob/image/asset/icwmp.gif"); } /** * TYNAMO-224 * * Discards all page persistent field changes if there is any exception in renderPageResponse */ @Match("PageResponseRenderer") public static void advisePageResponseRenderer(MethodAdviceReceiver receiver, final Logger logger) throws NoSuchMethodException { Method method = PageResponseRenderer.class.getMethod("renderPageResponse", Page.class); receiver.adviseMethod(method, new org.apache.tapestry5.plastic.MethodAdvice() { @Override public void advise(MethodInvocation methodInvocation) { try { methodInvocation.proceed(); } catch (RuntimeException e) { Page page = (Page) methodInvocation.getParameter(0); page.discardPersistentFieldChanges(); logger.info(String.format("discarding all %s page persistent field changes due to a %s", page.getName(), e.getClass().getSimpleName())); throw e; } } }); } @Contribute(ModuleManager.class) public static void setupJsModules(MappedConfiguration<String, Object> configuration, @Path("classpath:META-INF/assets/tynamo/model/composition.js") Resource composition) { configuration.add("composition", new JavaScriptModuleConfiguration(composition).dependsOn("jquery")); } }