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; 053 054/** 055 * Supplements the services defined by {@link org.apache.tapestry5.hibernate.modules.HibernateCoreModule} with additional 056 * services and configuration specific to Tapestry web application. 057 */ 058public class HibernateModule 059{ 060 public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration) 061 { 062 configuration.add(HibernateSymbols.PROVIDE_ENTITY_VALUE_ENCODERS, "true"); 063 configuration.add(HibernateSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED, "false"); 064 } 065 066 /** 067 * Contributes the package "<root>.entities" to the configuration, so that it will be scanned for annotated 068 * entity classes. 069 */ 070 public static void contributeHibernateEntityPackageManager(Configuration<String> configuration, 071 072 @Symbol(TapestryHttpInternalConstants.TAPESTRY_APP_PACKAGE_PARAM) 073 String appRootPackage) 074 { 075 configuration.add(appRootPackage + ".entities"); 076 } 077 078 @Contribute(ServiceOverride.class) 079 public static void provideInjectableSessionObject(MappedConfiguration<Class, Object> configuration, @HibernateCore 080 Session session) 081 { 082 configuration.add(Session.class, session); 083 } 084 085 /** 086 * Contributes {@link ValueEncoderFactory}s for all registered Hibernate entity classes. Encoding and decoding are 087 * based on the id property value of the entity using type coercion. Hence, if the id can be coerced to a String and 088 * back then the entity can be coerced. 089 */ 090 @SuppressWarnings("unchecked") 091 public static void contributeValueEncoderSource( 092 MappedConfiguration<Class, ValueEncoderFactory> configuration, 093 @Symbol(HibernateSymbols.PROVIDE_ENTITY_VALUE_ENCODERS) boolean provideEncoders, 094 final HibernateSessionSource sessionSource, final Session session, 095 final TypeCoercer typeCoercer, final PropertyAccess propertyAccess, 096 final LoggerSource loggerSource) 097 { 098 if (!provideEncoders) 099 return; 100 101 Set<EntityType<?>> entities = sessionSource.getSessionFactory().getMetamodel().getEntities(); 102 for (EntityType<?> entityType : entities) 103 { 104 Class<?> entityClass = entityType.getJavaType(); 105 if (entityClass != null) 106 { 107 SingularAttribute<?, ?> id = entityType.getId(entityType.getIdType().getJavaType()); 108 final String idenfierPropertyName = id.getName(); 109 ValueEncoderFactory factory = new ValueEncoderFactory() 110 { 111 @Override 112 public ValueEncoder create(Class type) 113 { 114 return new HibernateEntityValueEncoder(entityClass, idenfierPropertyName, 115 session, propertyAccess, typeCoercer, 116 loggerSource.getLogger(entityClass)); 117 } 118 }; 119 120 configuration.add(entityClass, factory); 121 122 } 123 } 124 } 125 126 /** 127 * Contributes the following: 128 * <dl> 129 * <dt>entity</dt> 130 * <dd>Stores the id of the entity and reloads from the {@link Session}</dd> 131 * </dl> 132 */ 133 public static void contributePersistentFieldManager( 134 MappedConfiguration<String, PersistentFieldStrategy> configuration) 135 { 136 configuration.addInstance(HibernatePersistenceConstants.ENTITY, EntityPersistentFieldStrategy.class); 137 } 138 139 /** 140 * Contributes the following strategy: 141 * <dl> 142 * <dt>entity</dt> 143 * <dd>Stores the id of the entity and reloads from the {@link Session}</dd> 144 * </dl> 145 */ 146 public void contributeApplicationStatePersistenceStrategySource( 147 MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration) 148 { 149 configuration 150 .addInstance(HibernatePersistenceConstants.ENTITY, EntityApplicationStatePersistenceStrategy.class); 151 } 152 153 /** 154 * Contributes {@link ApplicationStateContribution}s for all registered Hibernate entity classes. 155 * 156 * @param configuration 157 * Configuration to contribute 158 * @param entitySessionStatePersistenceStrategyEnabled 159 * indicates if contribution should take place 160 * @param sessionSource 161 * creates Hibernate session 162 */ 163 public static void contributeApplicationStateManager( 164 final MappedConfiguration<Class, ApplicationStateContribution> configuration, 165 @Symbol(HibernateSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED) 166 boolean entitySessionStatePersistenceStrategyEnabled, HibernateSessionSource sessionSource) 167 { 168 169 if (!entitySessionStatePersistenceStrategyEnabled) 170 return; 171 172 sessionSource.getSessionFactory().getMetamodel().getEntities().stream() 173 .map(EntityType::getJavaType) 174 .filter(Objects::nonNull) 175 .forEach(e -> configuration.add(e, new ApplicationStateContribution(HibernatePersistenceConstants.ENTITY))); 176 177 } 178 179 /** 180 * Adds the CommitAfter annotation work, to process the 181 * {@link org.apache.tapestry5.hibernate.annotations.CommitAfter} annotation. 182 */ 183 @Contribute(ComponentClassTransformWorker2.class) 184 @Primary 185 public static void provideCommitAfterAnnotationSupport( 186 OrderedConfiguration<ComponentClassTransformWorker2> configuration) 187 { 188 // If logging is enabled, we want logging to be the first advice, wrapping around the commit advice. 189 190 configuration.addInstance("CommitAfter", CommitAfterWorker.class, "after:Log"); 191 } 192 193 @Contribute(DashboardManager.class) 194 public static void provideHibernateDashboardTab(OrderedConfiguration<DashboardTab> configuration) 195 { 196 configuration.add("HibernateStatistics", new DashboardTab("Hibernate", "hibernate/HibernateStatistics"), "after:Services"); 197 } 198 199 @Contribute(ComponentClassResolver.class) 200 public static void provideLibraryMapping(Configuration<LibraryMapping> configuration) 201 { 202 configuration.add(new LibraryMapping("hibernate", "org.apache.tapestry5.hibernate.web")); 203 } 204}