/* * 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.net.URL; import java.util.List; import javax.persistence.spi.PersistenceUnitInfo; import javax.persistence.spi.PersistenceUnitTransactionType; import javax.sql.DataSource; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.validation.Schema; import org.seasar.extension.datasource.impl.SingletonDataSourceProxy; import org.seasar.extension.j2ee.JndiResourceLocator; import org.seasar.framework.container.S2Container; import org.seasar.framework.container.annotation.tiger.Binding; import org.seasar.framework.container.annotation.tiger.BindingType; import org.seasar.framework.env.Env; import org.seasar.framework.jpa.PersistenceUnitInfoFactory; import org.seasar.framework.util.InputStreamUtil; import org.seasar.framework.util.SAXParserFactoryUtil; import org.seasar.framework.util.SchemaUtil; import org.seasar.framework.util.StringUtil; import org.seasar.framework.util.URLUtil; import org.seasar.framework.util.tiger.CollectionsUtil; import org.seasar.framework.xml.SaxHandler; import org.seasar.framework.xml.SaxHandlerParser; import org.seasar.framework.xml.TagHandler; import org.seasar.framework.xml.TagHandlerContext; import org.seasar.framework.xml.TagHandlerRule; import org.xml.sax.Attributes; /** * 指定された<code>META-INF/persistence.xml</code>を読み込んで * {@link PersistenceUnitInfo 永続ユニット情報}を作成するファクトリの実装クラスです。 * * @author koichik */ public class PersistenceUnitInfoFactoryImpl implements PersistenceUnitInfoFactory { /** デフォルトの永続ユニットプロバイダクラス名 (Hibernate EntityManager) */ public static final String DEFAULT_PROVIDER = "org.hibernate.ejb.HibernatePersistence"; /** デフォルトのデータソース名 */ public static final String DEFAULT_DATASOURCE = "jdbc/dataSource"; /** <code>persistence.xml</code>のパス名 */ public static final String PERSISTENCE_XML = "META-INF/persistence.xml"; /** <code>persistence.xml</code>を検証するXML Schemaのパス名 */ public static final String PERSISTENCE_SCHEMA_NAME = "persistence_1_0.xsd"; /** 永続ユニットルートURLのコンテキストキー */ public static final String PERSISTENCE_UNIT_ROOT_URL = "persistenceUnitRootUrl"; /** <code>persistence.xml</code>をロードするクラスローダ */ protected ClassLoader classLoader; /** <code>persistence.xml</code>を検証するXML Schema */ protected Schema persistenceXmlSchema; /** S2コンテナ */ @Binding(bindingType = BindingType.MUST) protected S2Container container; /** データソースのプロクシを使う場合は<code>true</code> */ protected boolean useDataSourceProxy = Env.getValue().startsWith("ut"); /** デフォルトの永続ユニットプロバイダクラス名 */ protected String defaultProviderClassName = DEFAULT_PROVIDER; /** JTA用のデフォルトのデータソース名 */ protected String defaultJtaDataSource = DEFAULT_DATASOURCE; /** 非JTA用のデフォルトのデータソース名 */ protected String defaultNonJtaDataSource = DEFAULT_DATASOURCE; /** * コンテキストクラスローダを使用してインスタンスを構築します。 * */ public PersistenceUnitInfoFactoryImpl() { this(Thread.currentThread().getContextClassLoader()); } /** * 指定のクラスローダを使用してインスタンスを構築します。 * * @param classLoader * <code>persistence.xml</code>をロードするクラスローダ */ public PersistenceUnitInfoFactoryImpl(final ClassLoader classLoader) { this.classLoader = classLoader; final URL schemaUrl = classLoader.getResource(PERSISTENCE_SCHEMA_NAME); persistenceXmlSchema = SchemaUtil.newW3cXmlSchema(schemaUrl); } /** * データソースのプロクシを使う場合は<code>true</code>、それ以外の場合は<code>false</code>を設定します。 * * @param useDataSourceProxy * データソースのプロクシを使う場合は<code>true</code> */ @Binding(bindingType = BindingType.MAY) public void setUseDataSourceProxy(final boolean useDataSourceProxy) { this.useDataSourceProxy = useDataSourceProxy; } /** * デフォルトの永続ユニットプロバイダクラス名を設定します。 * * @param defaultProviderClassName * デフォルトの永続ユニットプロバイダクラス名 */ @Binding(bindingType = BindingType.MAY) public void setDefaultProviderClassName( final String defaultProviderClassName) { this.defaultProviderClassName = defaultProviderClassName; } /** * JTA用のデフォルトのデータソース名を設定します。 * * @param defaultJtaDataSource * JTA用のデフォルトのデータソース名 */ @Binding(bindingType = BindingType.MAY) public void setDefaultJtaDataSource(final String defaultJtaDataSource) { this.defaultJtaDataSource = defaultJtaDataSource; } /** * 非JTA用のデフォルトのデータソース名を設定します。 * * @param defaultNonJtaDataSource * 非JTA用のデフォルトのデータソース名 */ @Binding(bindingType = BindingType.MAY) public void setDefaultNonJtaDataSource(final String defaultNonJtaDataSource) { this.defaultNonJtaDataSource = defaultNonJtaDataSource; } public List<PersistenceUnitInfo> createPersistenceUnitInfo( final URL persistenceXmlUrl) { return createPersistenceUnitInfo(persistenceXmlUrl, toPersistenceUnitRootUrl(persistenceXmlUrl)); } @SuppressWarnings("unchecked") public List<PersistenceUnitInfo> createPersistenceUnitInfo( final URL persistenceXmlUrl, final URL persistenceUnitRootUrl) { final SaxHandlerParser parser = createSaxHandlerParser(persistenceUnitRootUrl); final InputStream is = URLUtil.openStream(persistenceXmlUrl); try { return (List<PersistenceUnitInfo>) parser.parse(is, persistenceUnitRootUrl.toExternalForm()); } finally { InputStreamUtil.close(is); } } /** * <code>META-INF/persistence.xml</code>のURLから永続ユニットのルートURLを求めて返します。 * * @param url * <code>META-INF/persistence.xml</code>のURL * @return 永続ユニットのルートURL */ protected static URL toPersistenceUnitRootUrl(final URL url) { final String s = url.toExternalForm(); if (!s.endsWith(PERSISTENCE_XML)) { throw new IllegalArgumentException(s); } final String rootUrl = s.substring(0, s.length() - PERSISTENCE_XML.length()); return URLUtil.create(url, rootUrl); } /** * XML Schemaを使用して妥当性を検証する{@link SaxHandlerParser}を作成します。 * * @param persistenceUnitRootUrl * 永続ユニットのルートURL * @return XML Schemaを使用して妥当性を検証する{@link SaxHandlerParser} */ protected SaxHandlerParser createSaxHandlerParser( final URL persistenceUnitRootUrl) { final SaxHandler handler = new SaxHandler( new PersistenceXmlTagHandlerRule()); final TagHandlerContext ctx = handler.getTagHandlerContext(); ctx.addParameter(PERSISTENCE_UNIT_ROOT_URL, persistenceUnitRootUrl); final SAXParserFactory factory = SAXParserFactoryUtil.newInstance(); factory.setNamespaceAware(true); factory.setSchema(persistenceXmlSchema); final SAXParser saxParser = SAXParserFactoryUtil.newSAXParser(factory); return new SaxHandlerParser(handler, saxParser); } private class PersistenceXmlTagHandlerRule extends TagHandlerRule { private static final long serialVersionUID = 1L; private PersistenceXmlTagHandlerRule() { addTagHandler("/persistence", new PersistenceTagHandler()); addTagHandler("persistence-unit", new PersistenceUnitTagHandler()); addTagHandler("provider", new ProviderTagHandler()); addTagHandler("jta-data-source", new JtaDataSourceTagHandler()); addTagHandler("non-jta-data-source", new NonJtaDataSourceTagHandler()); addTagHandler("mapping-file", new MappingFileTagHandler()); addTagHandler("jar-file", new JarFileTagHandler()); addTagHandler("class", new ClassTagHandler()); addTagHandler("exclude-unlisted-classes", new ExcludeUnlistedClassesTagHandler()); addTagHandler("property", new PropertyTagHandler()); } } private class DefaultTagHandler extends TagHandler { private static final long serialVersionUID = 1L; /** * 永続ユニット情報を返します。 * * @param context * コンテキスト * @return 永続ユニット情報 */ protected PersistenceUnitInfoImpl getPersistenceUnitInfo( final TagHandlerContext context) { return PersistenceUnitInfoImpl.class.cast(context.peek()); } /** * データソースを返します。 * * @param name * データソースのJNDI名 * @return データソース */ protected DataSource getDataSource(final String name) { if (useDataSourceProxy) { return new SingletonDataSourceProxy(name); } final String componentName = JndiResourceLocator.resolveName(name); return DataSource.class.cast(container.getComponent(componentName)); } /** * 永続ユニットのルートURLを返します。 * * @param context * コンテキスト * @return 永続ユニットのルートURL */ protected URL getPersistenceUnitRootURL(final TagHandlerContext context) { return URL.class.cast(context .getParameter(PERSISTENCE_UNIT_ROOT_URL)); } } private class PersistenceTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void start(final TagHandlerContext context, final Attributes attributes) { final List<PersistenceUnitInfo> list = CollectionsUtil .newArrayList(); context.push(list); } } private class PersistenceUnitTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void start(final TagHandlerContext context, final Attributes attributes) { final URL persistenceUnitRootUrl = URL.class.cast(context .getParameter(PERSISTENCE_UNIT_ROOT_URL)); final PersistenceUnitInfoImpl info = new PersistenceUnitInfoImpl( classLoader, persistenceUnitRootUrl); info.setPersistenceUnitName(attributes.getValue("name")); final String transactionType = attributes .getValue("transaction-type"); if (!StringUtil.isEmpty(transactionType)) { info.setTransactionType(PersistenceUnitTransactionType .valueOf(transactionType)); } context.push(info); } @SuppressWarnings("unchecked") @Override public void end(final TagHandlerContext context, final String body) { final PersistenceUnitInfoImpl info = PersistenceUnitInfoImpl.class .cast(context.pop()); if (StringUtil.isEmpty(info.getPersistenceProviderClassName())) { info.setPersistenceProviderClassName(defaultProviderClassName); } if (info.getJtaDataSource() == null) { info.setJtaDataSource(getDataSource(defaultJtaDataSource)); } if (info.getNonJtaDataSource() == null) { info .setNonJtaDataSource(getDataSource(defaultNonJtaDataSource)); } final List<PersistenceUnitInfo> list = List.class.cast(context .peek()); list.add(info); } } private class ProviderTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void end(final TagHandlerContext context, final String body) { getPersistenceUnitInfo(context).setPersistenceProviderClassName( body); } } private class JtaDataSourceTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void end(final TagHandlerContext context, final String body) { getPersistenceUnitInfo(context).setJtaDataSource( getDataSource(body)); } } private class NonJtaDataSourceTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void end(final TagHandlerContext context, final String body) { getPersistenceUnitInfo(context).setNonJtaDataSource( getDataSource(body)); } } private class MappingFileTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void end(final TagHandlerContext context, final String body) { getPersistenceUnitInfo(context).addMappingFileNames(body); } } @SuppressWarnings("unchecked") private class JarFileTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void end(final TagHandlerContext context, final String body) { final URL url = URLUtil.create(getPersistenceUnitRootURL(context), body); getPersistenceUnitInfo(context).addJarFileUrls(url); } } private class ClassTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void end(final TagHandlerContext context, final String body) { getPersistenceUnitInfo(context).addManagedClassNames(body); } } private class ExcludeUnlistedClassesTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void end(final TagHandlerContext context, final String body) { final String trimed = body.trim(); final boolean value = "true".equals(trimed) || "1".equals(trimed); getPersistenceUnitInfo(context).setExcludeUnlistedClasses(value); } } private class PropertyTagHandler extends DefaultTagHandler { private static final long serialVersionUID = 1L; @Override public void start(final TagHandlerContext context, final Attributes attributes) { final String name = attributes.getValue("name"); final String value = attributes.getValue("value"); getPersistenceUnitInfo(context).addProperties(name, value); } } }