001    // Copyright 2008, 2009 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    
015    package org.tynamo.jpa;
016    
017    import javax.persistence.EntityManager;
018    
019    import org.apache.tapestry5.ioc.MappedConfiguration;
020    import org.apache.tapestry5.ioc.OrderedConfiguration;
021    import org.apache.tapestry5.ioc.ScopeConstants;
022    import org.apache.tapestry5.ioc.ServiceBinder;
023    import org.apache.tapestry5.ioc.annotations.Inject;
024    import org.apache.tapestry5.ioc.annotations.Marker;
025    import org.apache.tapestry5.ioc.annotations.Scope;
026    import org.apache.tapestry5.ioc.annotations.Symbol;
027    import org.apache.tapestry5.ioc.services.PerthreadManager;
028    import org.apache.tapestry5.ioc.services.PropertyShadowBuilder;
029    import org.apache.tapestry5.ioc.services.RegistryShutdownHub;
030    import org.slf4j.Logger;
031    
032    import org.tynamo.jpa.internal.JPAEntityManagerSourceImpl;
033    import org.tynamo.jpa.internal.JPATransactionAdvisorImpl;
034    import org.tynamo.jpa.internal.JPATransactionDecoratorImpl;
035    import org.tynamo.jpa.internal.JPATransactionManagerImpl;
036    
037    /**
038     * Defines core services that support initialization of Hibernate and access to the Hibernate
039     * {@link javax.persistence.EntityManager}.
040     */
041    @SuppressWarnings( { "JavaDoc" })
042    @Marker(JPACore.class)
043    public class JPACoreModule
044    {
045            public static void bind(ServiceBinder binder)
046            {
047                    binder.bind(JPATransactionDecorator.class, JPATransactionDecoratorImpl.class);
048                    binder.bind(JPATransactionAdvisor.class, JPATransactionAdvisorImpl.class);
049                    // binder.bind(HibernateConfigurer.class,
050                    // JPAHibernateConfigurer.class).withId("JPAHibernateConfigurer");
051            }
052    
053            public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
054            {
055                    configuration.add(JPASymbols.DEFAULT_CONFIGURATION, "true");
056                    configuration.add(JPASymbols.EARLY_START_UP, "false");
057            }
058    
059            public static void contributeRegistryStartup(OrderedConfiguration<Runnable> configuration,
060    
061            @Symbol(JPASymbols.EARLY_START_UP) final boolean earlyStartup,
062    
063            final JPAEntityManagerSource entityManagerSource)
064            {
065                    configuration.add("JPAStartup", new Runnable()
066                    {
067                            public void run()
068                            {
069                                    if (earlyStartup)
070                                            entityManagerSource.create();
071                            }
072                    });
073            }
074    
075            /**
076             * The transaction manager manages transaction on a per-thread/per-request basis. Any active
077             * transaction will be rolled back at
078             * {@linkplain org.apache.tapestry5.ioc.Registry#cleanupThread() thread cleanup time}. The
079             * thread is cleaned up automatically in a Tapestry web application.
080             */
081            @Scope(ScopeConstants.PERTHREAD)
082            public static JPATransactionManager buildJPATransactionManager(JPAEntityManagerSource sessionSource,
083                    PerthreadManager perthreadManager)
084            {
085                    JPATransactionManagerImpl service = new JPATransactionManagerImpl(sessionSource);
086    
087                    perthreadManager.addThreadCleanupListener(service);
088    
089                    return service;
090            }
091    
092            public static EntityManager buildEntityManager(JPATransactionManager transactionManager,
093                    PropertyShadowBuilder propertyShadowBuilder)
094            {
095                    // Here's the thing: the tapestry.hibernate.Session class doesn't have to be per-thread,
096                    // since
097                    // it will invoke getSession() on the JPASessionManager service (which is per-thread).
098                    // On
099                    // first invocation per request,
100                    // this forces the HSM into existence (which creates the session and begins the
101                    // transaction).
102                    // Thus we don't actually create
103                    // a session until we first try to access it, then the session continues to exist for the
104                    // rest
105                    // of the request.
106    
107                    return propertyShadowBuilder.build(transactionManager, "entityManager", EntityManager.class);
108            }
109    
110            public static JPAEntityManagerSource buildJPAEntityManagerSource(Logger logger,
111                    @Inject @Symbol(JPASymbols.PERSISTENCE_UNIT) String persistenceUnit, RegistryShutdownHub hub)
112            {
113                    JPAEntityManagerSourceImpl hss = new JPAEntityManagerSourceImpl(logger, persistenceUnit);
114    
115                    hub.addRegistryShutdownListener(hss);
116    
117                    return hss;
118            }
119    }