001// Copyright 2011-2014 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.jpa.modules;
016
017import java.util.Collection;
018
019import javax.persistence.EntityManagerFactory;
020import javax.persistence.metamodel.EntityType;
021import javax.persistence.metamodel.Metamodel;
022import javax.persistence.spi.PersistenceUnitInfo;
023
024import org.apache.tapestry5.ValueEncoder;
025import org.apache.tapestry5.internal.InternalConstants;
026import org.apache.tapestry5.internal.jpa.CommitAfterWorker;
027import org.apache.tapestry5.internal.jpa.EntityApplicationStatePersistenceStrategy;
028import org.apache.tapestry5.internal.jpa.EntityManagerManagerImpl;
029import org.apache.tapestry5.internal.jpa.EntityManagerObjectProvider;
030import org.apache.tapestry5.internal.jpa.EntityManagerSourceImpl;
031import org.apache.tapestry5.internal.jpa.EntityPersistentFieldStrategy;
032import org.apache.tapestry5.internal.jpa.EntityTransactionManagerImpl;
033import org.apache.tapestry5.internal.jpa.JpaTransactionAdvisorImpl;
034import org.apache.tapestry5.internal.jpa.JpaValueEncoder;
035import org.apache.tapestry5.internal.jpa.PackageNamePersistenceUnitConfigurer;
036import org.apache.tapestry5.internal.jpa.PersistenceContextWorker;
037import org.apache.tapestry5.internal.services.PersistentFieldManager;
038import org.apache.tapestry5.ioc.Configuration;
039import org.apache.tapestry5.ioc.LoggerSource;
040import org.apache.tapestry5.ioc.MappedConfiguration;
041import org.apache.tapestry5.ioc.ObjectProvider;
042import org.apache.tapestry5.ioc.OrderedConfiguration;
043import org.apache.tapestry5.ioc.ScopeConstants;
044import org.apache.tapestry5.ioc.ServiceBinder;
045import org.apache.tapestry5.ioc.annotations.Contribute;
046import org.apache.tapestry5.ioc.annotations.Primary;
047import org.apache.tapestry5.ioc.annotations.Scope;
048import org.apache.tapestry5.ioc.annotations.Startup;
049import org.apache.tapestry5.ioc.annotations.Symbol;
050import org.apache.tapestry5.ioc.services.FactoryDefaults;
051import org.apache.tapestry5.ioc.services.MasterObjectProvider;
052import org.apache.tapestry5.ioc.services.PerthreadManager;
053import org.apache.tapestry5.ioc.services.PropertyAccess;
054import org.apache.tapestry5.ioc.services.SymbolProvider;
055import org.apache.tapestry5.ioc.services.TypeCoercer;
056import org.apache.tapestry5.jpa.EntityManagerManager;
057import org.apache.tapestry5.jpa.EntityManagerSource;
058import org.apache.tapestry5.jpa.EntityTransactionManager;
059import org.apache.tapestry5.jpa.JpaEntityPackageManager;
060import org.apache.tapestry5.jpa.JpaPersistenceConstants;
061import org.apache.tapestry5.jpa.JpaSymbols;
062import org.apache.tapestry5.jpa.JpaTransactionAdvisor;
063import org.apache.tapestry5.jpa.PersistenceUnitConfigurer;
064import org.apache.tapestry5.services.ApplicationStateContribution;
065import org.apache.tapestry5.services.ApplicationStateManager;
066import org.apache.tapestry5.services.ApplicationStatePersistenceStrategy;
067import org.apache.tapestry5.services.ApplicationStatePersistenceStrategySource;
068import org.apache.tapestry5.services.PersistentFieldStrategy;
069import org.apache.tapestry5.services.ValueEncoderFactory;
070import org.apache.tapestry5.services.ValueEncoderSource;
071import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
072import org.slf4j.Logger;
073
074/**
075 * Defines core services for JPA support.
076 *
077 * @since 5.3
078 */
079public class JpaModule
080{
081    public static void bind(final ServiceBinder binder)
082    {
083        binder.bind(JpaTransactionAdvisor.class, JpaTransactionAdvisorImpl.class);
084        binder.bind(PersistenceUnitConfigurer.class, PackageNamePersistenceUnitConfigurer.class).withSimpleId();
085        binder.bind(EntityManagerSource.class, EntityManagerSourceImpl.class);
086        binder.bind(EntityTransactionManager.class, EntityTransactionManagerImpl.class);
087
088    }
089
090    public static JpaEntityPackageManager buildJpaEntityPackageManager(final Collection<String> packageNames)
091    {
092        return new JpaEntityPackageManager()
093        {
094            @Override
095            public Collection<String> getPackageNames()
096            {
097                return packageNames;
098            }
099        };
100    }
101
102    @Scope(ScopeConstants.PERTHREAD)
103    public static EntityManagerManager buildEntityManagerManager(final EntityManagerSource entityManagerSource,
104                                                                 final PerthreadManager perthreadManager, final Logger logger)
105    {
106        final EntityManagerManagerImpl service = new EntityManagerManagerImpl(entityManagerSource, logger);
107
108        perthreadManager.addThreadCleanupListener(service);
109
110        return service;
111    }
112
113    @Contribute(JpaEntityPackageManager.class)
114    public static void provideEntityPackages(Configuration<String> configuration,
115
116                                             @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
117                                             String appRootPackage)
118    {
119        configuration.add(appRootPackage + ".entities");
120    }
121
122    @Contribute(PersistentFieldManager.class)
123    public static void provideEntityPersistentFieldStrategies(
124            final MappedConfiguration<String, PersistentFieldStrategy> configuration)
125    {
126        configuration.addInstance(JpaPersistenceConstants.ENTITY, EntityPersistentFieldStrategy.class);
127    }
128
129    @Contribute(ApplicationStatePersistenceStrategySource.class)
130    public void provideApplicationStatePersistenceStrategies(
131            final MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration)
132    {
133        configuration.addInstance(JpaPersistenceConstants.ENTITY, EntityApplicationStatePersistenceStrategy.class);
134    }
135
136    @Contribute(ComponentClassTransformWorker2.class)
137    @Primary
138    public static void provideClassTransformWorkers(OrderedConfiguration<ComponentClassTransformWorker2> configuration)
139    {
140        configuration.addInstance("PersistenceContext", PersistenceContextWorker.class, "after:Property");
141        configuration.addInstance("JPACommitAfter", CommitAfterWorker.class, "after:Log");
142    }
143
144    @Contribute(MasterObjectProvider.class)
145    public static void provideObjectProviders(final OrderedConfiguration<ObjectProvider> configuration)
146    {
147        configuration.addInstance("EntityManager", EntityManagerObjectProvider.class,
148                "before:AnnotationBasedContributions");
149    }
150
151    @Contribute(SymbolProvider.class)
152    @FactoryDefaults
153    public static void provideFactoryDefaults(final MappedConfiguration<String, String> configuration)
154    {
155        configuration.add(JpaSymbols.PROVIDE_ENTITY_VALUE_ENCODERS, "true");
156        configuration.add(JpaSymbols.EARLY_START_UP, "true");
157        configuration.add(JpaSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED, "true");
158        configuration.add(JpaSymbols.PERSISTENCE_DESCRIPTOR, "/META-INF/persistence.xml");
159    }
160
161    @Contribute(ValueEncoderSource.class)
162    public static void provideValueEncoders(final MappedConfiguration<Class, ValueEncoderFactory> configuration,
163                                            @Symbol(JpaSymbols.PROVIDE_ENTITY_VALUE_ENCODERS)
164                                            final boolean provideEncoders, final EntityManagerSource entityManagerSource,
165                                            final EntityManagerManager entityManagerManager, final TypeCoercer typeCoercer,
166                                            final PropertyAccess propertyAccess, final LoggerSource loggerSource)
167    {
168
169        if (!provideEncoders)
170            return;
171
172        for (final PersistenceUnitInfo info : entityManagerSource.getPersistenceUnitInfos())
173        {
174            final EntityManagerFactory emf = entityManagerSource.getEntityManagerFactory(info.getPersistenceUnitName());
175
176            final Metamodel metamodel = emf.getMetamodel();
177
178            for (final EntityType<?> entity : metamodel.getEntities())
179            {
180                final Class<?> javaType = entity.getJavaType();
181
182                final ValueEncoderFactory factory = new ValueEncoderFactory()
183                {
184                    @Override
185                    public ValueEncoder create(final Class type)
186                    {
187                        return new JpaValueEncoder(entity, entityManagerManager, info.getPersistenceUnitName(),
188                                propertyAccess, typeCoercer, loggerSource.getLogger(javaType));
189                    }
190                };
191
192                configuration.add(javaType, factory);
193            }
194        }
195    }
196
197    @Contribute(ApplicationStateManager.class)
198    public static void provideApplicationStateContributions(
199            final MappedConfiguration<Class, ApplicationStateContribution> configuration,
200            final EntityManagerSource entityManagerSource,
201            @Symbol(JpaSymbols.ENTITY_SESSION_STATE_PERSISTENCE_STRATEGY_ENABLED)
202            final boolean entitySessionStatePersistenceStrategyEnabled)
203    {
204
205        if (!entitySessionStatePersistenceStrategyEnabled)
206            return;
207
208        for (final PersistenceUnitInfo info : entityManagerSource.getPersistenceUnitInfos())
209        {
210            final EntityManagerFactory emf = entityManagerSource.getEntityManagerFactory(info.getPersistenceUnitName());
211
212            final Metamodel metamodel = emf.getMetamodel();
213
214            for (EntityType<?> entity : metamodel.getEntities())
215            {
216                configuration.add(entity.getJavaType(), new ApplicationStateContribution(JpaPersistenceConstants.ENTITY));
217            }
218
219        }
220    }
221
222    @Startup
223    public static void startupEarly(final EntityManagerManager entityManagerManager, @Symbol(JpaSymbols.EARLY_START_UP)
224    final boolean earlyStartup)
225    {
226        if (earlyStartup)
227        {
228            entityManagerManager.getEntityManagers();
229        }
230    }
231}