    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 "&lt;root&gt;.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}