001// Copyright 2007-2013 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.hibernate.web.modules; 016 017import java.util.Objects; 018import java.util.Set; 019 020import javax.persistence.metamodel.EntityType; 021import javax.persistence.metamodel.SingularAttribute; 022 023import org.apache.tapestry5.ValueEncoder; 024import org.apache.tapestry5.commons.Configuration; 025import org.apache.tapestry5.commons.MappedConfiguration; 026import org.apache.tapestry5.commons.OrderedConfiguration; 027import org.apache.tapestry5.commons.services.PropertyAccess; 028import org.apache.tapestry5.commons.services.TypeCoercer; 029import org.apache.tapestry5.hibernate.HibernateCore; 030import org.apache.tapestry5.hibernate.HibernateSessionSource; 031import org.apache.tapestry5.hibernate.HibernateSymbols; 032import org.apache.tapestry5.hibernate.web.HibernatePersistenceConstants; 033import org.apache.tapestry5.hibernate.web.internal.CommitAfterWorker; 034import org.apache.tapestry5.hibernate.web.internal.EntityApplicationStatePersistenceStrategy; 035import org.apache.tapestry5.hibernate.web.internal.EntityPersistentFieldStrategy; 036import org.apache.tapestry5.hibernate.web.internal.HibernateEntityValueEncoder; 037import org.apache.tapestry5.http.internal.TapestryHttpInternalConstants; 038import org.apache.tapestry5.ioc.LoggerSource; 039import org.apache.tapestry5.ioc.annotations.Contribute; 040import org.apache.tapestry5.ioc.annotations.Primary; 041import org.apache.tapestry5.ioc.annotations.Symbol; 042import org.apache.tapestry5.ioc.services.ServiceOverride; 043import org.apache.tapestry5.services.ApplicationStateContribution; 044import org.apache.tapestry5.services.ApplicationStatePersistenceStrategy; 045import org.apache.tapestry5.services.ComponentClassResolver; 046import org.apache.tapestry5.services.LibraryMapping; 047import org.apache.tapestry5.services.PersistentFieldStrategy; 048import org.apache.tapestry5.services.ValueEncoderFactory; 049import org.apache.tapestry5.services.dashboard.DashboardManager; 050import org.apache.tapestry5.services.dashboard.DashboardTab; 051import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2; 052import org.hibernate.Session; 053import org.slf4j.Logger; 054 055/** 056 * Supplements the services defined by {@link org.apache.tapestry5.hibernate.modules.HibernateCoreModule} with additional 057 * services and configuration specific to Tapestry web application. 058 */ 059public class HibernateModule 060{ 061 public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration) 062 { 063 configuration.add(HibernateSymbols.PROVIDE_ENTITY_VALUE_ENCODERS, "true"); 064 configuration.add(HibernateSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED, "false"); 065 } 066 067 /** 068 * Contributes the package "<root>.entities" to the configuration, so that it will be scanned for annotated 069 * entity classes. 070 */ 071 public static void contributeHibernateEntityPackageManager(Configuration<String> configuration, 072 073 @Symbol(TapestryHttpInternalConstants.TAPESTRY_APP_PACKAGE_PARAM) 074 String appRootPackage) 075 { 076 configuration.add(appRootPackage + ".entities"); 077 } 078 079 @Contribute(ServiceOverride.class) 080 public static void provideInjectableSessionObject(MappedConfiguration<Class, Object> configuration, @HibernateCore 081 Session session) 082 { 083 configuration.add(Session.class, session); 084 } 085 086 /** 087 * Contributes {@link ValueEncoderFactory}s for all registered Hibernate entity classes. Encoding and decoding are 088 * based on the id property value of the entity using type coercion. Hence, if the id can be coerced to a String and 089 * back then the entity can be coerced. 090 */ 091 @SuppressWarnings("unchecked") 092 public static void contributeValueEncoderSource( 093 MappedConfiguration<Class, ValueEncoderFactory> configuration, 094 @Symbol(HibernateSymbols.PROVIDE_ENTITY_VALUE_ENCODERS) boolean provideEncoders, 095 final HibernateSessionSource sessionSource, final Session session, 096 final TypeCoercer typeCoercer, final PropertyAccess propertyAccess, 097 final LoggerSource loggerSource, final Logger logger) 098 { 099 if (!provideEncoders) 100 return; 101 102 Set<EntityType<?>> entities = sessionSource.getSessionFactory().getMetamodel().getEntities(); 103 for (EntityType<?> entityType : entities) 104 { 105 Class<?> entityClass = entityType.getJavaType(); 106 if (entityClass != null) 107 { 108 if (entityType.hasSingleIdAttribute()) 109 { 110 SingularAttribute<?, ?> id = entityType.getId(entityType.getIdType().getJavaType()); 111 final String idenfierPropertyName = id.getName(); 112 ValueEncoderFactory factory = new ValueEncoderFactory() 113 { 114 @Override 115 public ValueEncoder create(Class type) 116 { 117 return new HibernateEntityValueEncoder(entityClass, idenfierPropertyName, 118 session, propertyAccess, typeCoercer, 119 loggerSource.getLogger(entityClass)); 120 } 121 }; 122 123 configuration.add(entityClass, factory); 124 } else { 125 logger.warn("Not creating a value encoder for {} as it does not have a single id attribute.", entityClass); 126 } 127 } 128 } 129 } 130 131 /** 132 * Contributes the following: 133 * <dl> 134 * <dt>entity</dt> 135 * <dd>Stores the id of the entity and reloads from the {@link Session}</dd> 136 * </dl> 137 */ 138 public static void contributePersistentFieldManager( 139 MappedConfiguration<String, PersistentFieldStrategy> configuration) 140 { 141 configuration.addInstance(HibernatePersistenceConstants.ENTITY, EntityPersistentFieldStrategy.class); 142 } 143 144 /** 145 * Contributes the following strategy: 146 * <dl> 147 * <dt>entity</dt> 148 * <dd>Stores the id of the entity and reloads from the {@link Session}</dd> 149 * </dl> 150 */ 151 public void contributeApplicationStatePersistenceStrategySource( 152 MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration) 153 { 154 configuration 155 .addInstance(HibernatePersistenceConstants.ENTITY, EntityApplicationStatePersistenceStrategy.class); 156 } 157 158 /** 159 * Contributes {@link ApplicationStateContribution}s for all registered Hibernate entity classes. 160 * 161 * @param configuration 162 * Configuration to contribute 163 * @param entitySessionStatePersistenceStrategyEnabled 164 * indicates if contribution should take place 165 * @param sessionSource 166 * creates Hibernate session 167 */ 168 public static void contributeApplicationStateManager( 169 final MappedConfiguration<Class, ApplicationStateContribution> configuration, 170 @Symbol(HibernateSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED) 171 boolean entitySessionStatePersistenceStrategyEnabled, HibernateSessionSource sessionSource) 172 { 173 174 if (!entitySessionStatePersistenceStrategyEnabled) 175 return; 176 177 sessionSource.getSessionFactory().getMetamodel().getEntities().stream() 178 .map(EntityType::getJavaType) 179 .filter(Objects::nonNull) 180 .forEach(e -> configuration.add(e, new ApplicationStateContribution(HibernatePersistenceConstants.ENTITY))); 181 182 } 183 184 /** 185 * Adds the CommitAfter annotation work, to process the 186 * {@link org.apache.tapestry5.hibernate.annotations.CommitAfter} annotation. 187 */ 188 @Contribute(ComponentClassTransformWorker2.class) 189 @Primary 190 public static void provideCommitAfterAnnotationSupport( 191 OrderedConfiguration<ComponentClassTransformWorker2> configuration) 192 { 193 // If logging is enabled, we want logging to be the first advice, wrapping around the commit advice. 194 195 configuration.addInstance("CommitAfter", CommitAfterWorker.class, "after:Log"); 196 } 197 198 @Contribute(DashboardManager.class) 199 public static void provideHibernateDashboardTab(OrderedConfiguration<DashboardTab> configuration) 200 { 201 configuration.add("HibernateStatistics", new DashboardTab("Hibernate", "hibernate/HibernateStatistics"), "after:Services"); 202 } 203 204 @Contribute(ComponentClassResolver.class) 205 public static void provideLibraryMapping(Configuration<LibraryMapping> configuration) 206 { 207 configuration.add(new LibraryMapping("hibernate", "org.apache.tapestry5.hibernate.web")); 208 } 209}