    001    // Copyright 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
    015    package org.apache.tapestry5.internal.jpa;
    016
    017    import org.apache.tapestry5.func.F;
    018    import org.apache.tapestry5.func.Mapper;
    019    import org.apache.tapestry5.func.Predicate;
    020    import org.apache.tapestry5.ioc.Resource;
    021    import org.apache.tapestry5.ioc.annotations.Local;
    022    import org.apache.tapestry5.ioc.annotations.PostInjection;
    023    import org.apache.tapestry5.ioc.annotations.Symbol;
    024    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    025    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    026    import org.apache.tapestry5.ioc.services.RegistryShutdownHub;
    027    import org.apache.tapestry5.jpa.*;
    028    import org.slf4j.Logger;
    029
    030    import javax.persistence.EntityManager;
    031    import javax.persistence.EntityManagerFactory;
    032    import javax.persistence.spi.PersistenceProvider;
    033    import javax.persistence.spi.PersistenceProviderResolver;
    034    import javax.persistence.spi.PersistenceProviderResolverHolder;
    035    import javax.persistence.spi.PersistenceUnitInfo;
    036    import java.io.InputStream;
    037    import java.util.Collections;
    038    import java.util.List;
    039    import java.util.Map;
    040    import java.util.Map.Entry;
    041    import java.util.Set;
    042
    043    public class EntityManagerSourceImpl implements EntityManagerSource
    044    {
    045        private final Map<String, EntityManagerFactory> entityManagerFactories = CollectionFactory
    046                .newMap();
    047
    048        private final Logger logger;
    049
    050        private final List<TapestryPersistenceUnitInfo> persistenceUnitInfos;
    051
    052        public EntityManagerSourceImpl(Logger logger, @Symbol(JpaSymbols.PERSISTENCE_DESCRIPTOR)
    053        final Resource persistenceDescriptor, @Local
    054        PersistenceUnitConfigurer packageNamePersistenceUnitConfigurer,
    055                                       Map<String, PersistenceUnitConfigurer> configuration)
    056        {
    057            this.logger = logger;
    058
    059            List<TapestryPersistenceUnitInfo> persistenceUnitInfos = parsePersistenceUnitInfos(persistenceDescriptor);
    060
    061            final Map<String, PersistenceUnitConfigurer> remainingConfigurations = configure(configuration, persistenceUnitInfos);
    062
    063            configureRemaining(persistenceUnitInfos, remainingConfigurations);
    064
    065            if (persistenceUnitInfos.size() == 1)
    066            {
    067                packageNamePersistenceUnitConfigurer.configure(persistenceUnitInfos.get(0));
    068            } else
    069            {
    070                validateUnitInfos(persistenceUnitInfos);
    071            }
    072
    073            this.persistenceUnitInfos = persistenceUnitInfos;
    074        }
    075
    076        @PostInjection
    077        public void listenForShutdown(RegistryShutdownHub hub)
    078        {
    079            hub.addRegistryShutdownListener(new Runnable()
    080            {
    081                public void run()
    082                {
    083                    registryDidShutdown();
    084                }
    085            });
    086        }
    087
    088        private void validateUnitInfos(List<TapestryPersistenceUnitInfo> persistenceUnitInfos)
    089        {
    090            final List<String> affectedUnits = F.flow(persistenceUnitInfos).filter(new Predicate<TapestryPersistenceUnitInfo>()
    091            {
    092                public boolean accept(TapestryPersistenceUnitInfo info)
    093                {
    094                    return !info.excludeUnlistedClasses();
    095                }
    096            }).map(new Mapper<TapestryPersistenceUnitInfo, String>()
    097            {
    098                public String map(TapestryPersistenceUnitInfo info)
    099                {
    100                    return info.getPersistenceUnitName();
    101                }
    102            }).toList();
    103
    104            if (0 < affectedUnits.size())
    105            {
    106                throw new RuntimeException(
    107                        String.format(
    108                                "Persistence units '%s' are configured to include managed classes that have not been explicitly listed. " +
    109                                        "This is forbidden when multiple persistence units are used in the same application. " +
    110                                        "Please configure persistence units to exclude unlisted managed classes (e.g. by removing <exclude-unlisted-classes> element) " +
    111                                        "and include them explicitly.",
    112                                InternalUtils.join(affectedUnits)));
    113            }
    114        }
    115
    116        private List<TapestryPersistenceUnitInfo> parsePersistenceUnitInfos(Resource persistenceDescriptor)
    117        {
    118            List<TapestryPersistenceUnitInfo> persistenceUnitInfos = CollectionFactory.newList();
    119
    120            if (persistenceDescriptor.exists())
    121            {
    122                final PersistenceParser parser = new PersistenceParser();
    123
    124                InputStream inputStream = null;
    125                try
    126                {
    127                    inputStream = persistenceDescriptor.openStream();
    128                    persistenceUnitInfos = parser.parse(inputStream);
    129                } catch (Exception e)
    130                {
    131                    throw new RuntimeException(e);
    132                } finally
    133                {
    134                    InternalUtils.close(inputStream);
    135                }
    136
    137            }
    138            return persistenceUnitInfos;
    139        }
    140
    141        private Map<String, PersistenceUnitConfigurer> configure(Map<String, PersistenceUnitConfigurer> configuration, List<TapestryPersistenceUnitInfo> persistenceUnitInfos)
    142        {
    143            final Map<String, PersistenceUnitConfigurer> remainingConfigurations = CollectionFactory.newMap(configuration);
    144
    145            for (final TapestryPersistenceUnitInfo info : persistenceUnitInfos)
    146            {
    147                final String unitName = info.getPersistenceUnitName();
    148
    149                final PersistenceUnitConfigurer configurer = configuration.get(unitName);
    150
    151                if (configurer != null)
    152                {
    153                    configurer.configure(info);
    154
    155                    remainingConfigurations.remove(unitName);
    156                }
    157            }
    158
    159            return remainingConfigurations;
    160        }
    161
    162
    163        private void configureRemaining(List<TapestryPersistenceUnitInfo> persistenceUnitInfos, Map<String, PersistenceUnitConfigurer> remainingConfigurations)
    164        {
    165            for (Entry<String, PersistenceUnitConfigurer> entry : remainingConfigurations.entrySet())
    166            {
    167                final PersistenceUnitInfoImpl info = new PersistenceUnitInfoImpl(entry.getKey());
    168
    169                final PersistenceUnitConfigurer configurer = entry.getValue();
    170                configurer.configure(info);
    171
    172                persistenceUnitInfos.add(info);
    173            }
    174        }
    175
    176        /**
    177         * {@inheritDoc}
    178         */
    179        public EntityManagerFactory getEntityManagerFactory(final String persistenceUnitName)
    180        {
    181            EntityManagerFactory emf = entityManagerFactories.get(persistenceUnitName);
    182
    183            if (emf == null)
    184            {
    185                emf = createEntityManagerFactory(persistenceUnitName);
    186
    187                entityManagerFactories.put(persistenceUnitName, emf);
    188            }
    189
    190            return emf;
    191        }
    192
    193        @SuppressWarnings("unchecked")
    194        EntityManagerFactory createEntityManagerFactory(final String persistenceUnitName)
    195        {
    196
    197            for (final TapestryPersistenceUnitInfo info : persistenceUnitInfos)
    198            {
    199                if (info.getPersistenceUnitName().equals(persistenceUnitName))
    200                {
    201                    final Map properties = info.getEntityManagerProperties() == null ? CollectionFactory.newCaseInsensitiveMap() : info.getEntityManagerProperties();
    202                    properties.put(JpaConstants.PERSISTENCE_UNIT_NAME, persistenceUnitName);
    203
    204                    String providerClassName = info.getPersistenceProviderClassName();
    205
    206                    final PersistenceProvider persistenceProvider = getPersistenceProvider(persistenceUnitName, providerClassName);
    207
    208                    return persistenceProvider.createContainerEntityManagerFactory(info, properties);
    209                }
    210            }
    211
    212            throw new IllegalStateException(String.format(
    213                    "Failed to create EntityManagerFactory for persistence unit '%s'",
    214                    persistenceUnitName));
    215        }
    216
    217        private PersistenceProvider getPersistenceProvider(final String persistenceUnitName, final String providerClassName)
    218        {
    219            final PersistenceProviderResolver resolver = PersistenceProviderResolverHolder
    220                    .getPersistenceProviderResolver();
    221
    222            final List<PersistenceProvider> providers = resolver.getPersistenceProviders();
    223
    224            if (providers.isEmpty())
    225            {
    226                throw new IllegalStateException(
    227                        "No PersistenceProvider implementation available in the runtime environment.");
    228            }
    229
    230            if(1 < providers.size() && providerClassName == null)
    231            {
    232                throw new IllegalStateException(
    233                        String.format("Persistence providers [%s] are available in the runtime environment " +
    234                                "but no provider class is defined for the persistence unit %s.", InternalUtils.join(toProviderClasses(providers)), persistenceUnitName));
    235            }
    236
    237            if(providerClassName != null)
    238            {
    239                return findPersistenceProviderByName(providers, providerClassName);
    240            }
    241
    242            return providers.get(0);
    243        }
    244
    245        private PersistenceProvider findPersistenceProviderByName(final List<PersistenceProvider> providers, final String providerClassName)
    246        {
    247            PersistenceProvider provider = F.flow(providers).filter(new Predicate<PersistenceProvider>() {
    248                public boolean accept(PersistenceProvider next) {
    249                    return next.getClass().getName().equals(providerClassName);
    250                }
    251            }).first();
    252
    253            if(provider == null)
    254            {
    255                throw new IllegalStateException(
    256                        String.format("No persistence provider of type %s found in the runtime environment. " +
    257                                "Following providers are available: [%s]", providerClassName, InternalUtils.join(toProviderClasses(providers))));
    258            }
    259
    260            return provider;
    261        }
    262
    263        private List<Class> toProviderClasses(final List<PersistenceProvider> providers)
    264        {
    265           return F.flow(providers).map(new Mapper<PersistenceProvider, Class>() {
    266               public Class map(PersistenceProvider element) {
    267                   return element.getClass();
    268               }
    269           }).toList();
    270        }
    271
    272        public EntityManager create(final String persistenceUnitName)
    273        {
    274            return getEntityManagerFactory(persistenceUnitName).createEntityManager();
    275        }
    276
    277        private void registryDidShutdown()
    278        {
    279            final Set<Entry<String, EntityManagerFactory>> entrySet = entityManagerFactories.entrySet();
    280
    281            for (final Entry<String, EntityManagerFactory> entry : entrySet)
    282            {
    283                final EntityManagerFactory emf = entry.getValue();
    284                try
    285                {
    286                    emf.close();
    287                } catch (final Exception e)
    288                {
    289                    logger.error(String.format(
    290                            "Failed to close EntityManagerFactory for persistence unit '%s'",
    291                            entry.getKey()), e);
    292                }
    293            }
    294
    295            entityManagerFactories.clear();
    296
    297        }
    298
    299        public List<PersistenceUnitInfo> getPersistenceUnitInfos()
    300        {
    301            return Collections.<PersistenceUnitInfo>unmodifiableList(persistenceUnitInfos);
    302        }
    303
    304    }