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.modules;
016
017import org.apache.tapestry5.ValueEncoder;
018import org.apache.tapestry5.hibernate.HibernateCore;
019import org.apache.tapestry5.hibernate.HibernatePersistenceConstants;
020import org.apache.tapestry5.hibernate.HibernateSessionSource;
021import org.apache.tapestry5.hibernate.HibernateSymbols;
022import org.apache.tapestry5.internal.InternalConstants;
023import org.apache.tapestry5.internal.hibernate.CommitAfterWorker;
024import org.apache.tapestry5.internal.hibernate.EntityApplicationStatePersistenceStrategy;
025import org.apache.tapestry5.internal.hibernate.EntityPersistentFieldStrategy;
026import org.apache.tapestry5.internal.hibernate.HibernateEntityValueEncoder;
027import org.apache.tapestry5.ioc.Configuration;
028import org.apache.tapestry5.ioc.LoggerSource;
029import org.apache.tapestry5.ioc.MappedConfiguration;
030import org.apache.tapestry5.ioc.OrderedConfiguration;
031import org.apache.tapestry5.ioc.annotations.Contribute;
032import org.apache.tapestry5.ioc.annotations.Primary;
033import org.apache.tapestry5.ioc.annotations.Symbol;
034import org.apache.tapestry5.ioc.services.PropertyAccess;
035import org.apache.tapestry5.ioc.services.ServiceOverride;
036import org.apache.tapestry5.ioc.services.TypeCoercer;
037import org.apache.tapestry5.services.ApplicationStateContribution;
038import org.apache.tapestry5.services.ApplicationStatePersistenceStrategy;
039import org.apache.tapestry5.services.PersistentFieldStrategy;
040import org.apache.tapestry5.services.ValueEncoderFactory;
041import org.apache.tapestry5.services.dashboard.DashboardManager;
042import org.apache.tapestry5.services.dashboard.DashboardTab;
043import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
044import org.hibernate.Session;
045import org.hibernate.mapping.PersistentClass;
046
047import java.util.Iterator;
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(InternalConstants.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(MappedConfiguration<Class, ValueEncoderFactory> configuration,
087                                                    @Symbol(HibernateSymbols.PROVIDE_ENTITY_VALUE_ENCODERS)
088                                                    boolean provideEncoders, final HibernateSessionSource sessionSource, final Session session,
089                                                    final TypeCoercer typeCoercer, final PropertyAccess propertyAccess, final LoggerSource loggerSource)
090    {
091        if (!provideEncoders)
092            return;
093
094        org.hibernate.cfg.Configuration config = sessionSource.getConfiguration();
095        Iterator<PersistentClass> mappings = config.getClassMappings();
096        while (mappings.hasNext())
097        {
098            final PersistentClass persistentClass = mappings.next();
099            final Class entityClass = persistentClass.getMappedClass();
100
101            if (entityClass != null)
102            {
103                ValueEncoderFactory factory = new ValueEncoderFactory()
104                {
105                    @Override
106                    public ValueEncoder create(Class type)
107                    {
108                        return new HibernateEntityValueEncoder(entityClass, persistentClass, session, propertyAccess,
109                                typeCoercer, loggerSource.getLogger(entityClass));
110                    }
111                };
112
113                configuration.add(entityClass, factory);
114
115            }
116        }
117    }
118
119    /**
120     * Contributes the following:
121     * <dl>
122     * <dt>entity</dt>
123     * <dd>Stores the id of the entity and reloads from the {@link Session}</dd>
124     * </dl>
125     */
126    public static void contributePersistentFieldManager(
127            MappedConfiguration<String, PersistentFieldStrategy> configuration)
128    {
129        configuration.addInstance(HibernatePersistenceConstants.ENTITY, EntityPersistentFieldStrategy.class);
130    }
131
132    /**
133     * Contributes the following strategy:
134     * <dl>
135     * <dt>entity</dt>
136     * <dd>Stores the id of the entity and reloads from the {@link Session}</dd>
137     * </dl>
138     */
139    public void contributeApplicationStatePersistenceStrategySource(
140            MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration)
141    {
142        configuration
143                .addInstance(HibernatePersistenceConstants.ENTITY, EntityApplicationStatePersistenceStrategy.class);
144    }
145
146    /**
147     * Contributes {@link ApplicationStateContribution}s for all registered Hibernate entity classes.
148     *
149     * @param configuration
150     *         Configuration to contribute
151     * @param entitySessionStatePersistenceStrategyEnabled
152     *         indicates if contribution should take place
153     * @param sessionSource
154     *         creates Hibernate session
155     */
156    public static void contributeApplicationStateManager(
157            MappedConfiguration<Class, ApplicationStateContribution> configuration,
158            @Symbol(HibernateSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED)
159            boolean entitySessionStatePersistenceStrategyEnabled, HibernateSessionSource sessionSource)
160    {
161
162        if (!entitySessionStatePersistenceStrategyEnabled)
163            return;
164
165        org.hibernate.cfg.Configuration config = sessionSource.getConfiguration();
166        Iterator<PersistentClass> mappings = config.getClassMappings();
167        while (mappings.hasNext())
168        {
169
170            final PersistentClass persistentClass = mappings.next();
171            final Class entityClass = persistentClass.getMappedClass();
172
173            configuration.add(entityClass, new ApplicationStateContribution(HibernatePersistenceConstants.ENTITY));
174        }
175    }
176
177    /**
178     * Adds the CommitAfter annotation work, to process the
179     * {@link org.apache.tapestry5.hibernate.annotations.CommitAfter} annotation.
180     */
181    @Contribute(ComponentClassTransformWorker2.class)
182    @Primary
183    public static void provideCommitAfterAnnotationSupport(
184            OrderedConfiguration<ComponentClassTransformWorker2> configuration)
185    {
186        // If logging is enabled, we want logging to be the first advice, wrapping around the commit advice.
187
188        configuration.addInstance("CommitAfter", CommitAfterWorker.class, "after:Log");
189    }
190
191    @Contribute(DashboardManager.class)
192    public static void provideHibernateDashboardTab(OrderedConfiguration<DashboardTab> configuration)
193    {
194        configuration.add("HibernateStatistics", new DashboardTab("Hibernate", "core/HibernateStatistics"), "after:Services");
195    }
196}