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