JPA persistence.xml files may use jar-file entries relative to the unit root (as per the JPA spec)

Issue: SPR-9797
This commit is contained in:
Juergen Hoeller 2012-10-10 23:32:40 +02:00 committed by unknown
parent 92a92b7937
commit efd872e35a
3 changed files with 93 additions and 61 deletions

View File

@ -173,12 +173,11 @@ class PersistenceUnitReader {
Element persistence = document.getDocumentElement(); Element persistence = document.getDocumentElement();
String version = persistence.getAttribute(PERSISTENCE_VERSION); String version = persistence.getAttribute(PERSISTENCE_VERSION);
URL unitRootURL = determinePersistenceUnitRootUrl(resource); URL rootUrl = determinePersistenceUnitRootUrl(resource);
List<Element> units = DomUtils.getChildElementsByTagName(persistence, PERSISTENCE_UNIT); List<Element> units = DomUtils.getChildElementsByTagName(persistence, PERSISTENCE_UNIT);
for (Element unit : units) { for (Element unit : units) {
SpringPersistenceUnitInfo info = parsePersistenceUnitInfo(unit, version); infos.add(parsePersistenceUnitInfo(unit, version, rootUrl));
info.setPersistenceUnitRootUrl(unitRootURL);
infos.add(info);
} }
return infos; return infos;
@ -193,15 +192,14 @@ class PersistenceUnitReader {
*/ */
protected URL determinePersistenceUnitRootUrl(Resource resource) throws IOException { protected URL determinePersistenceUnitRootUrl(Resource resource) throws IOException {
URL originalURL = resource.getURL(); URL originalURL = resource.getURL();
String urlToString = originalURL.toExternalForm();
// If we get an archive, simply return the jar URL (section 6.2 from the JPA spec) // If we get an archive, simply return the jar URL (section 6.2 from the JPA spec)
if (ResourceUtils.isJarURL(originalURL)) { if (ResourceUtils.isJarURL(originalURL)) {
return ResourceUtils.extractJarFileURL(originalURL); return ResourceUtils.extractJarFileURL(originalURL);
} }
else {
// check META-INF folder // check META-INF folder
String urlToString = originalURL.toExternalForm();
if (!urlToString.contains(META_INF)) { if (!urlToString.contains(META_INF)) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(resource.getFilename() + logger.info(resource.getFilename() +
@ -220,19 +218,26 @@ class PersistenceUnitReader {
} }
String persistenceUnitRoot = urlToString.substring(0, urlToString.lastIndexOf(META_INF)); String persistenceUnitRoot = urlToString.substring(0, urlToString.lastIndexOf(META_INF));
return new URL(persistenceUnitRoot); if (persistenceUnitRoot.endsWith("/")) {
persistenceUnitRoot = persistenceUnitRoot.substring(0, persistenceUnitRoot.length() - 1);
} }
return new URL(persistenceUnitRoot);
} }
/** /**
* Parse the unit info DOM element. * Parse the unit info DOM element.
*/ */
protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(Element persistenceUnit, String version) throws IOException { protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(Element persistenceUnit, String version, URL rootUrl)
throws IOException {
SpringPersistenceUnitInfo unitInfo = new SpringPersistenceUnitInfo(); SpringPersistenceUnitInfo unitInfo = new SpringPersistenceUnitInfo();
// set JPA version (1.0 or 2.0) // set JPA version (1.0 or 2.0)
unitInfo.setPersistenceXMLSchemaVersion(version); unitInfo.setPersistenceXMLSchemaVersion(version);
// set persistence unit root URL
unitInfo.setPersistenceUnitRootUrl(rootUrl);
// set unit name // set unit name
unitInfo.setPersistenceUnitName(persistenceUnit.getAttribute(UNIT_NAME).trim()); unitInfo.setPersistenceUnitName(persistenceUnit.getAttribute(UNIT_NAME).trim());
@ -277,10 +282,10 @@ class PersistenceUnitReader {
unitInfo.setValidationModeName(validationMode); unitInfo.setValidationModeName(validationMode);
} }
parseProperties(persistenceUnit, unitInfo);
parseManagedClasses(persistenceUnit, unitInfo);
parseMappingFiles(persistenceUnit, unitInfo); parseMappingFiles(persistenceUnit, unitInfo);
parseJarFiles(persistenceUnit, unitInfo); parseJarFiles(persistenceUnit, unitInfo);
parseClass(persistenceUnit, unitInfo);
parseProperty(persistenceUnit, unitInfo);
return unitInfo; return unitInfo;
} }
@ -289,7 +294,7 @@ class PersistenceUnitReader {
* Parse the <code>property</code> XML elements. * Parse the <code>property</code> XML elements.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void parseProperty(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) { protected void parseProperties(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
Element propRoot = DomUtils.getChildElementByTagName(persistenceUnit, PROPERTIES); Element propRoot = DomUtils.getChildElementByTagName(persistenceUnit, PROPERTIES);
if (propRoot == null) { if (propRoot == null) {
return; return;
@ -306,7 +311,7 @@ class PersistenceUnitReader {
* Parse the <code>class</code> XML elements. * Parse the <code>class</code> XML elements.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void parseClass(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) { protected void parseManagedClasses(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
List<Element> classes = DomUtils.getChildElementsByTagName(persistenceUnit, MANAGED_CLASS_NAME); List<Element> classes = DomUtils.getChildElementsByTagName(persistenceUnit, MANAGED_CLASS_NAME);
for (Element element : classes) { for (Element element : classes) {
String value = DomUtils.getTextValue(element).trim(); String value = DomUtils.getTextValue(element).trim();
@ -315,23 +320,6 @@ class PersistenceUnitReader {
} }
} }
/**
* Parse the <code>jar-file</code> XML elements.
*/
@SuppressWarnings("unchecked")
protected void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
List<Element> jars = DomUtils.getChildElementsByTagName(persistenceUnit, JAR_FILE_URL);
for (Element element : jars) {
String value = DomUtils.getTextValue(element).trim();
if (StringUtils.hasText(value)) {
Resource[] resources = this.resourcePatternResolver.getResources(value);
for (Resource resource : resources) {
unitInfo.addJarFileUrl(resource.getURL());
}
}
}
}
/** /**
* Parse the <code>mapping-file</code> XML elements. * Parse the <code>mapping-file</code> XML elements.
*/ */
@ -346,4 +334,36 @@ class PersistenceUnitReader {
} }
} }
/**
* Parse the <code>jar-file</code> XML elements.
*/
@SuppressWarnings("unchecked")
protected void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
List<Element> jars = DomUtils.getChildElementsByTagName(persistenceUnit, JAR_FILE_URL);
for (Element element : jars) {
String value = DomUtils.getTextValue(element).trim();
if (StringUtils.hasText(value)) {
Resource[] resources = this.resourcePatternResolver.getResources(value);
boolean found = false;
for (Resource resource : resources) {
if (resource.exists()) {
found = true;
unitInfo.addJarFileUrl(resource.getURL());
}
}
if (!found) {
// relative to the persistence unit root, according to the JPA spec
URL rootUrl = unitInfo.getPersistenceUnitRootUrl();
if (rootUrl != null) {
unitInfo.addJarFileUrl(new URL(rootUrl, value));
}
else {
logger.warn("Cannot resolve jar-file entry [" + value + "] in persistence unit '" +
unitInfo.getPersistenceUnitName() + "' without root URL");
}
}
}
}
}
} }

View File

@ -1,5 +1,8 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0"> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="OrderManagement"/> <persistence-unit name="OrderManagement">
<jar-file>order.jar</jar-file>
<jar-file>../../../order-supplemental.jar</jar-file>
</persistence-unit>
</persistence> </persistence>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,7 +24,6 @@ import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType; import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource; import javax.sql.DataSource;
import static org.junit.Assert.*;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -37,13 +36,32 @@ import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.jdbc.datasource.lookup.MapDataSourceLookup; import org.springframework.jdbc.datasource.lookup.MapDataSourceLookup;
import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import static org.junit.Assert.*;
/** /**
* Unit and integration tests for the JPA XML resource parsing support. * Unit and integration tests for the JPA XML resource parsing support.
* *
* @author Costin Leau * @author Costin Leau
* @author Juergen Hoeller
*/ */
public class PersistenceXmlParsingTests { public class PersistenceXmlParsingTests {
@Test
public void testMetaInfCase() throws Exception {
PersistenceUnitReader reader = new PersistenceUnitReader(
new PathMatchingResourcePatternResolver(), new JndiDataSourceLookup());
String resource = "/org/springframework/orm/jpa/META-INF/persistence.xml";
PersistenceUnitInfo[] info = reader.readPersistenceUnitInfos(resource);
assertNotNull(info);
assertEquals(1, info.length);
assertEquals("OrderManagement", info[0].getPersistenceUnitName());
assertEquals(2, info[0].getJarFileUrls().size());
assertEquals(new ClassPathResource("order.jar").getURL(), info[0].getJarFileUrls().get(0));
assertEquals(new ClassPathResource("order-supplemental.jar").getURL(), info[0].getJarFileUrls().get(1));
}
@Test @Test
public void testExample1() throws Exception { public void testExample1() throws Exception {
PersistenceUnitReader reader = new PersistenceUnitReader( PersistenceUnitReader reader = new PersistenceUnitReader(
@ -87,6 +105,7 @@ public class PersistenceXmlParsingTests {
assertEquals(2, info[0].getJarFileUrls().size()); assertEquals(2, info[0].getJarFileUrls().size());
assertEquals(new ClassPathResource("order.jar").getURL(), info[0].getJarFileUrls().get(0)); assertEquals(new ClassPathResource("order.jar").getURL(), info[0].getJarFileUrls().get(0));
assertEquals(new ClassPathResource("order-supplemental.jar").getURL(), info[0].getJarFileUrls().get(1)); assertEquals(new ClassPathResource("order-supplemental.jar").getURL(), info[0].getJarFileUrls().get(1));
assertEquals(0, info[0].getProperties().keySet().size()); assertEquals(0, info[0].getProperties().keySet().size());
assertNull(info[0].getJtaDataSource()); assertNull(info[0].getJtaDataSource());
assertNull(info[0].getNonJtaDataSource()); assertNull(info[0].getNonJtaDataSource());
@ -120,12 +139,6 @@ public class PersistenceXmlParsingTests {
assertSame(PersistenceUnitTransactionType.RESOURCE_LOCAL, info[0].getTransactionType()); assertSame(PersistenceUnitTransactionType.RESOURCE_LOCAL, info[0].getTransactionType());
assertEquals(0, info[0].getProperties().keySet().size()); assertEquals(0, info[0].getProperties().keySet().size());
// TODO this is undefined as yet. Do we look up Spring datasource?
// assertNotNull(info[0].getNonJtaDataSource());
//
// assertEquals(ds .toString(),
// info[0].getNonJtaDataSource().toString());
builder.clear(); builder.clear();
} }
@ -202,9 +215,6 @@ public class PersistenceXmlParsingTests {
assertEquals(1, pu2.getMappingFileNames().size()); assertEquals(1, pu2.getMappingFileNames().size());
assertEquals("order2.xml", pu2.getMappingFileNames().get(0)); assertEquals("order2.xml", pu2.getMappingFileNames().get(0));
@SuppressWarnings("unused")
Ignore ignore; // the following assertions fail only during coverage runs
/*
assertEquals(1, pu2.getJarFileUrls().size()); assertEquals(1, pu2.getJarFileUrls().size());
assertEquals(new ClassPathResource("order-supplemental.jar").getURL(), pu2.getJarFileUrls().get(0)); assertEquals(new ClassPathResource("order-supplemental.jar").getURL(), pu2.getJarFileUrls().get(0));
assertTrue(pu2.excludeUnlistedClasses()); assertTrue(pu2.excludeUnlistedClasses());
@ -213,7 +223,6 @@ public class PersistenceXmlParsingTests {
// TODO need to define behaviour with non jta datasource // TODO need to define behaviour with non jta datasource
assertEquals(ds, pu2.getNonJtaDataSource()); assertEquals(ds, pu2.getNonJtaDataSource());
*/
} }
@Test @Test
@ -265,7 +274,7 @@ public class PersistenceXmlParsingTests {
assertNull(url); assertNull(url);
url = reader.determinePersistenceUnitRootUrl(new ClassPathResource("/org/springframework/orm/jpa/META-INF/persistence.xml")); url = reader.determinePersistenceUnitRootUrl(new ClassPathResource("/org/springframework/orm/jpa/META-INF/persistence.xml"));
assertTrue("the containing folder should have been returned", url.toString().endsWith("/org/springframework/orm/jpa/")); assertTrue("the containing folder should have been returned", url.toString().endsWith("/org/springframework/orm/jpa"));
} }
@Test @Test