/* * Copyright 2004-2015 the Seasar Foundation and the Others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.seasar.framework.jpa.impl; import java.io.InputStream; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import javax.persistence.EntityManagerFactory; import javax.persistence.spi.PersistenceProvider; import javax.persistence.spi.PersistenceUnitInfo; import org.seasar.framework.container.annotation.tiger.Binding; import org.seasar.framework.container.annotation.tiger.BindingType; import org.seasar.framework.jpa.PersistenceClassTransformer; import org.seasar.framework.jpa.PersistenceUnitConfiguration; import org.seasar.framework.jpa.PersistenceUnitInfoRegistry; import org.seasar.framework.jpa.util.TransformClassLoader; import org.seasar.framework.log.Logger; import org.seasar.framework.util.ClassUtil; import org.seasar.framework.util.StringUtil; import org.seasar.framework.util.ClassTraversal.ClassHandler; import org.seasar.framework.util.ResourceTraversal.ResourceHandler; import org.seasar.framework.util.tiger.CollectionsUtil; import org.seasar.framework.util.tiger.ReflectionUtil; /** * コンテナ管理の{@link EntityManagerFactory} を提供するクラスです。 * * @author taedium */ public class ContainerPersistenceUnitProvider extends AbstractPersistenceUnitProvider { private static final Logger logger = Logger .getLogger(ContainerPersistenceUnitProvider.class); /** 永続ユニットのコンフィギュレーション */ protected PersistenceUnitConfiguration persistenceUnitConfiguration; /** 永続ユニット情報のレジストリ */ protected PersistenceUnitInfoRegistry persistenceUnitInfoRegistry; /** 永続クラスのトランスフォーマ */ protected PersistenceClassTransformer persistenceClassTransformer; /** 永続プロバイダのクラス名 */ protected String providerClassName; /** 永続プロバイダのプロパティ */ protected Map<String, String> properties = CollectionsUtil.newHashMap(); /** * 永続ユニットのコンフィギュレーションを設定します。 * * @param persistenceUnitConfiguration * 永続ユニットのコンフィギュレーション */ @Binding(bindingType = BindingType.MUST) public void setPersistenceUnitConfiguration( final PersistenceUnitConfiguration persistenceUnitConfiguration) { this.persistenceUnitConfiguration = persistenceUnitConfiguration; } /** * 永続ユニット情報のレジストリを設定します。 * * @param persistenceUnitInfoRegistry * 永続ユニット情報のレジストリ */ @Binding(bindingType = BindingType.MUST) public void setPersistenceUnitInfoRegistry( final PersistenceUnitInfoRegistry persistenceUnitInfoRegistry) { this.persistenceUnitInfoRegistry = persistenceUnitInfoRegistry; } /** * 永続クラスのトランスフォーマを設定します。 * * @param persistenceClassTransformer * 永続クラスのトランスフォーマ */ @Binding(bindingType = BindingType.MUST) public void setPersistenceClassTransformer( final PersistenceClassTransformer persistenceClassTransformer) { this.persistenceClassTransformer = persistenceClassTransformer; } /** * 永続プロバイダのクラス名を設定します。 * <p> * このプロパティに値が設定されると<code>persistence.xml</code>に設定されている情報を上書きします。 * </p> * * @param providerClassName * 永続プロバイダのクラス名 */ @Binding(bindingType = BindingType.MAY) public void setProviderClassName(final String providerClassName) { this.providerClassName = providerClassName; } /** * 永続ユニット情報のプロパティを設定します。 * <p> * このプロパティに値が設定されると<code>persistence.xml</code>に設定されている情報に追加されます。 * </p> * * @param properties * 永続ユニット情報のプロパティ */ @Binding(bindingType = BindingType.MAY) public void setProperties(final Map<String, String> properties) { this.properties.putAll(properties); } public EntityManagerFactory createEntityManagerFactory( final String abstractUnitName, final String concreteUnitName) { final PersistenceUnitInfoImpl unitInfo = (PersistenceUnitInfoImpl) persistenceUnitInfoRegistry .getPersistenceUnitInfo(concreteUnitName); if (unitInfo == null) { throw new IllegalArgumentException(concreteUnitName); // TODO } overrideUnitInfo(unitInfo); addMappingFiles(abstractUnitName, unitInfo); addPersistenceClasses(abstractUnitName, unitInfo); final PersistenceProvider provider = createPersistenceProvider(unitInfo); final EntityManagerFactory emf = createEntityManagerFactory(provider, unitInfo); persistenceClassTransformer.transform(unitInfo); return emf; } /** * 永続ユニット情報の設定を上書きします。 * * @param unitInfo * 永続ユニット情報 */ protected void overrideUnitInfo(final PersistenceUnitInfoImpl unitInfo) { if (!StringUtil.isEmpty(providerClassName)) { unitInfo.setPersistenceProviderClassName(providerClassName); } for (final Entry<String, String> entry : properties.entrySet()) { unitInfo.addProperties(entry.getKey(), entry.getValue()); } } /** * マッピングファイルを永続ユニット情報に登録します。 * * @param abstractUnitName * 抽象永続ユニット名 * @param unitInfo * 永続ユニット情報 */ protected void addMappingFiles(final String abstractUnitName, final PersistenceUnitInfo unitInfo) { persistenceUnitConfiguration.detectMappingFiles(abstractUnitName, new MappingFileHandler(unitInfo)); } /** * 永続クラスを永続ユニット情報に登録します。 * * @param abstractUnitName * 抽象永続ユニット名 * @param unitInfo * 永続ユニット情報 */ protected void addPersistenceClasses(final String abstractUnitName, final PersistenceUnitInfo unitInfo) { final ClassLoader original = Thread.currentThread() .getContextClassLoader(); Thread.currentThread().setContextClassLoader( unitInfo.getNewTempClassLoader()); try { persistenceUnitConfiguration.detectPersistenceClasses( abstractUnitName, new PersistenceClassHandler(unitInfo)); } finally { Thread.currentThread().setContextClassLoader(original); } } /** * 永続ユニット情報から永続プロバイダを作成します。 * * @param unitInfo * 永続ユニット情報 * @return 永続プロバイダ */ protected PersistenceProvider createPersistenceProvider( final PersistenceUnitInfo unitInfo) { final String providerClassName = unitInfo .getPersistenceProviderClassName(); final Class<PersistenceProvider> providerClass = ReflectionUtil .forName(providerClassName); return ReflectionUtil.newInstance(providerClass); } /** * エンティティマネジャーファクトリを作成します。 * * @param provider * 永続プロバイダ * @param unitInfo * 永続ユニット情報 * @return エンティティマネジャーファクトリ */ protected EntityManagerFactory createEntityManagerFactory( final PersistenceProvider provider, final PersistenceUnitInfoImpl unitInfo) { final ClassLoader original = Thread.currentThread() .getContextClassLoader(); final TransformClassLoader transLoader = unitInfo .getTransformClassLoader(); transLoader .registerPersistenceClassTransformer(persistenceClassTransformer); transLoader.registerPersistenceUnitInfo(unitInfo); Thread.currentThread().setContextClassLoader(transLoader); try { return provider.createContainerEntityManagerFactory(unitInfo, null); } finally { Thread.currentThread().setContextClassLoader(original); } } /** * マッピングファイルを永続ユニット情報に登録するクラスです。 * * @author taedium */ public static class MappingFileHandler implements ResourceHandler { /** 永続ユニット情報 */ protected PersistenceUnitInfo unitInfo; /** * インスタンスを構築します。 * * @param unitInfo * 永続ユニット */ public MappingFileHandler(final PersistenceUnitInfo unitInfo) { this.unitInfo = unitInfo; } public void processResource(final String path, final InputStream is) { if (logger.isDebugEnabled()) { logger.log("DSSR0112", new Object[] { path, unitInfo.getPersistenceUnitName() }); } unitInfo.getMappingFileNames().add(path); } } /** * 永続クラスを永続ユニット情報に登録するクラスです。 * * @author taedium */ public static class PersistenceClassHandler implements ClassHandler { /** 永続ユニット情報 */ protected PersistenceUnitInfo unitInfo; /** 処理済みパッケージ名のセット */ protected final Set<String> packageNames = CollectionsUtil.newHashSet(); /** * インスタンスを構築します。 * * @param unitInfo * 永続ユニット */ public PersistenceClassHandler(final PersistenceUnitInfo unitInfo) { this.unitInfo = unitInfo; } public void processClass(final String packageName, final String shortClassName) { final String className = ClassUtil.concatName(packageName, shortClassName); if (logger.isDebugEnabled()) { logger.log("DSSR0113", new Object[] { className, unitInfo.getPersistenceUnitName() }); } unitInfo.getManagedClassNames().add(className); if (!packageNames.contains(packageName)) { addPackageInfo(packageName); } } /** * 永続ユニット情報にパッケージ名を追加します。 * <p> * パッケージに<code>package-info</code>クラスが存在する場合はパッケージ名を永続クラスとして永続ユニット情報に追加します。 * </p> * * @param packageName * パッケージ名 */ protected void addPackageInfo(final String packageName) { packageNames.add(packageName); final String pkgInfoName = ClassUtil.concatName(packageName, "package-info"); final Class<?> pkgInfoClass = ReflectionUtil .forNameNoException(pkgInfoName); if (pkgInfoClass != null && pkgInfoClass.getPackage() != null) { unitInfo.getManagedClassNames().add(packageName); } } } }