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}