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