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