KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > orm > jpa > persistenceunit > PersistenceUnitReader


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.orm.jpa.persistenceunit;
18
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.net.URL JavaDoc;
22 import java.util.LinkedList JavaDoc;
23 import java.util.List JavaDoc;
24
25 import javax.persistence.spi.PersistenceUnitTransactionType;
26 import javax.xml.XMLConstants JavaDoc;
27 import javax.xml.parsers.DocumentBuilder JavaDoc;
28 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
29 import javax.xml.parsers.ParserConfigurationException JavaDoc;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.w3c.dom.Document JavaDoc;
34 import org.w3c.dom.Element JavaDoc;
35 import org.xml.sax.ErrorHandler JavaDoc;
36 import org.xml.sax.SAXException JavaDoc;
37
38 import org.springframework.core.io.Resource;
39 import org.springframework.core.io.support.ResourcePatternResolver;
40 import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
41 import org.springframework.util.Assert;
42 import org.springframework.util.ResourceUtils;
43 import org.springframework.util.StringUtils;
44 import org.springframework.util.xml.DomUtils;
45 import org.springframework.util.xml.SimpleSaxErrorHandler;
46
47 /**
48  * Internal helper class for reading <code>persistence.xml</code> files.
49  *
50  * @author Costin Leau
51  * @author Juergen Hoeller
52  * @since 2.0
53  */

54 class PersistenceUnitReader {
55
56     private static final String JavaDoc MAPPING_FILE_NAME = "mapping-file";
57
58     private static final String JavaDoc JAR_FILE_URL = "jar-file";
59
60     private static final String JavaDoc MANAGED_CLASS_NAME = "class";
61
62     private static final String JavaDoc PROPERTIES = "properties";
63
64     private static final String JavaDoc PROVIDER = "provider";
65
66     private static final String JavaDoc EXCLUDE_UNLISTED_CLASSES = "exclude-unlisted-classes";
67
68     private static final String JavaDoc NON_JTA_DATA_SOURCE = "non-jta-data-source";
69
70     private static final String JavaDoc JTA_DATA_SOURCE = "jta-data-source";
71
72     private static final String JavaDoc TRANSACTION_TYPE = "transaction-type";
73
74     private static final String JavaDoc PERSISTENCE_UNIT = "persistence-unit";
75
76     private static final String JavaDoc UNIT_NAME = "name";
77
78     private static final String JavaDoc SCHEMA_NAME = "persistence_1_0.xsd";
79
80     private static final String JavaDoc JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
81
82     private static final String JavaDoc JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
83
84     private static final String JavaDoc META_INF = "META-INF";
85
86
87     private final Log logger = LogFactory.getLog(getClass());
88
89     private final ResourcePatternResolver resourcePatternResolver;
90
91     private final DataSourceLookup dataSourceLookup;
92
93
94     /**
95      * Create a new PersistenceUnitReader.
96      * @param resourcePatternResolver the ResourcePatternResolver to use for loading resources
97      * @param dataSourceLookup the DataSourceLookup to resolve DataSource names in
98      * <code>persistence.xml</code> files against
99      */

100     public PersistenceUnitReader(ResourcePatternResolver resourcePatternResolver, DataSourceLookup dataSourceLookup) {
101         Assert.notNull(resourcePatternResolver, "ResourceLoader must not be null");
102         Assert.notNull(dataSourceLookup, "DataSourceLookup must not be null");
103         this.resourcePatternResolver = resourcePatternResolver;
104         this.dataSourceLookup = dataSourceLookup;
105     }
106
107
108     /**
109      * Parse and build all persistence unit infos defined in the specified XML file(s).
110      * @param persistenceXmlLocation the resource location (can be a pattern)
111      * @return the resulting PersistenceUnitInfo instances
112      */

113     public SpringPersistenceUnitInfo[] readPersistenceUnitInfos(String JavaDoc persistenceXmlLocation) {
114         return readPersistenceUnitInfos(new String JavaDoc[] {persistenceXmlLocation});
115     }
116
117     /**
118      * Parse and build all persistence unit infos defined in the given XML files.
119      * @param persistenceXmlLocations the resource locations (can be patterns)
120      * @return the resulting PersistenceUnitInfo instances
121      */

122     public SpringPersistenceUnitInfo[] readPersistenceUnitInfos(String JavaDoc[] persistenceXmlLocations) {
123         ErrorHandler JavaDoc handler = new SimpleSaxErrorHandler(logger);
124         List JavaDoc<SpringPersistenceUnitInfo> infos = new LinkedList JavaDoc<SpringPersistenceUnitInfo>();
125         String JavaDoc resourceLocation = null;
126         try {
127             for (int i = 0; i < persistenceXmlLocations.length; i++) {
128                 Resource[] resources = this.resourcePatternResolver.getResources(persistenceXmlLocations[i]);
129                 for (Resource resource : resources) {
130                     resourceLocation = resource.toString();
131                     InputStream JavaDoc stream = resource.getInputStream();
132                     try {
133                         Document JavaDoc document = validateResource(handler, stream);
134                         parseDocument(resource, document, infos);
135                     }
136                     finally {
137                         stream.close();
138                     }
139                 }
140             }
141         }
142         catch (IOException JavaDoc ex) {
143             throw new IllegalArgumentException JavaDoc("Cannot parse persistence unit from " + resourceLocation, ex);
144         }
145         catch (SAXException JavaDoc ex) {
146             throw new IllegalArgumentException JavaDoc("Invalid XML in persistence unit from " + resourceLocation, ex);
147         }
148         catch (ParserConfigurationException JavaDoc ex) {
149             throw new IllegalArgumentException JavaDoc("Internal error parsing persistence unit from " + resourceLocation);
150         }
151
152         return infos.toArray(new SpringPersistenceUnitInfo[infos.size()]);
153     }
154
155
156     /**
157      * Validate the given stream and return a valid DOM document for parsing.
158      */

159     protected Document JavaDoc validateResource(ErrorHandler JavaDoc handler, InputStream JavaDoc stream)
160             throws ParserConfigurationException JavaDoc, SAXException JavaDoc, IOException JavaDoc {
161
162         DocumentBuilderFactory JavaDoc dbf = DocumentBuilderFactory.newInstance();
163         dbf.setNamespaceAware(true);
164
165         // Set schema location only if we found one inside the classpath.
166
Resource schemaLocation = findSchemaResource(SCHEMA_NAME);
167         if (schemaLocation != null) {
168             if (logger.isDebugEnabled()) {
169                 logger.debug("Found schema resource: " + schemaLocation.getURL());
170             }
171             dbf.setValidating(true);
172             dbf.setAttribute(JAXP_SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI);
173             dbf.setAttribute(JAXP_SCHEMA_SOURCE, schemaLocation.getURL().toString());
174         }
175         else {
176             logger.debug("Schema resource [" + SCHEMA_NAME +
177                     "] not found - falling back to XML parsing without schema validation");
178         }
179
180         DocumentBuilder JavaDoc parser = dbf.newDocumentBuilder();
181         parser.setErrorHandler(handler);
182         return parser.parse(stream);
183     }
184
185     /**
186      * Try to locate the schema first in the class path before using the URL specified inside the XML.
187      * @return an existing resource, or <code>null</code> if none found
188      */

189     protected Resource findSchemaResource(String JavaDoc schemaName) {
190         try {
191             // First search the class path root (TopLink)
192
Resource schemaLocation = this.resourcePatternResolver.getResource("classpath:" + schemaName);
193             if (schemaLocation.exists()) {
194                 return schemaLocation;
195             }
196
197             // Search org packages (open source provider such as Hibernate or OpenJPA)
198
Resource[] resources = this.resourcePatternResolver.getResources("classpath*:org/**/" + schemaName);
199             if (resources.length > 0) {
200                 return resources[0];
201             }
202
203             // Search com packages (some commercial provider)
204
resources = this.resourcePatternResolver.getResources("classpath*:com/**/" + schemaName);
205             if (resources.length > 0) {
206                 return resources[0];
207             }
208
209             // Finally, do a lookup for unpacked files.
210
// See the warning in
211
// org.springframework.core.io.support.PathMatchingResourcePatternResolver
212
// for more info on this strategy.
213
resources = this.resourcePatternResolver.getResources("classpath*:**/" + schemaName);
214             if (resources.length > 0) {
215                 return resources[0];
216             }
217         }
218         catch (IOException JavaDoc ex) {
219             if (logger.isDebugEnabled()) {
220                 logger.debug("Could not search for JPA schema resource [" + schemaName + "] in class path", ex);
221             }
222         }
223         return null;
224     }
225
226
227     /**
228      * Parse the validated document and populates(add to) the given unit info
229      * list.
230      */

231     protected List JavaDoc<SpringPersistenceUnitInfo> parseDocument(
232             Resource resource, Document JavaDoc document, List JavaDoc<SpringPersistenceUnitInfo> infos) throws IOException JavaDoc {
233
234         Element JavaDoc persistence = document.getDocumentElement();
235         URL JavaDoc unitRootURL = determinePersistenceUnitRootUrl(resource);
236         List JavaDoc<Element JavaDoc> units = (List JavaDoc<Element JavaDoc>) DomUtils.getChildElementsByTagName(persistence, PERSISTENCE_UNIT);
237         for (Element JavaDoc unit : units) {
238             SpringPersistenceUnitInfo info = parsePersistenceUnitInfo(unit);
239             info.setPersistenceUnitRootUrl(unitRootURL);
240             infos.add(info);
241         }
242
243         return infos;
244     }
245
246     /**
247      * Determine the persistence unit root URL based on the given resource
248      * (which points to the <code>persistence.xml</code> file we're reading).
249      * @param resource the resource to check
250      * @return the corresponding persistence unit root URL
251      * @throws IOException if the checking failed
252      */

253     protected URL JavaDoc determinePersistenceUnitRootUrl(Resource resource) throws IOException JavaDoc {
254         URL JavaDoc originalURL = resource.getURL();
255         String JavaDoc urlToString = originalURL.toExternalForm();
256
257         // If we get an archive, simply return the jar URL (section 6.2 from the JPA spec)
258
if (ResourceUtils.isJarURL(originalURL)) {
259             return ResourceUtils.extractJarFileURL(originalURL);
260         }
261
262         else {
263             // check META-INF folder
264
if (!urlToString.contains(META_INF)) {
265                 if (logger.isInfoEnabled()) {
266                     logger.info(resource.getFilename() +
267                             " should be located inside META-INF directory; cannot determine persistence unit root URL for " +
268                             resource);
269                 }
270                 return null;
271             }
272             if (urlToString.lastIndexOf(META_INF) == urlToString.lastIndexOf('/') - (1 + META_INF.length())) {
273                 if (logger.isInfoEnabled()) {
274                     logger.info(resource.getFilename() +
275                             " is not located in the root of META-INF directory; cannot determine persistence unit root URL for " +
276                             resource);
277                 }
278                 return null;
279             }
280
281             String JavaDoc persistenceUnitRoot = urlToString.substring(0, urlToString.lastIndexOf(META_INF));
282             return new URL JavaDoc(persistenceUnitRoot);
283         }
284     }
285
286     /**
287      * Parse the unit info DOM element.
288      */

289     protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(Element JavaDoc persistenceUnit) throws IOException JavaDoc {
290         SpringPersistenceUnitInfo unitInfo = new SpringPersistenceUnitInfo();
291
292         // set unit name
293
unitInfo.setPersistenceUnitName(persistenceUnit.getAttribute(UNIT_NAME).trim());
294
295         // set transaction type
296
String JavaDoc txType = persistenceUnit.getAttribute(TRANSACTION_TYPE).trim();
297         if (StringUtils.hasText(txType)) {
298             unitInfo.setTransactionType(PersistenceUnitTransactionType.valueOf(txType));
299         }
300
301         // data-source
302
String JavaDoc jtaDataSource = DomUtils.getChildElementValueByTagName(persistenceUnit, JTA_DATA_SOURCE);
303         if (StringUtils.hasText(jtaDataSource)) {
304             unitInfo.setJtaDataSource(this.dataSourceLookup.getDataSource(jtaDataSource.trim()));
305         }
306
307         String JavaDoc nonJtaDataSource = DomUtils.getChildElementValueByTagName(persistenceUnit, NON_JTA_DATA_SOURCE);
308         if (StringUtils.hasText(nonJtaDataSource)) {
309             unitInfo.setNonJtaDataSource(this.dataSourceLookup.getDataSource(nonJtaDataSource.trim()));
310         }
311
312         // provider
313
String JavaDoc provider = DomUtils.getChildElementValueByTagName(persistenceUnit, PROVIDER);
314         if (StringUtils.hasText(provider)) {
315             unitInfo.setPersistenceProviderClassName(provider.trim());
316         }
317
318         // exclude unlisted classes
319
Element JavaDoc excludeUnlistedClasses = DomUtils.getChildElementByTagName(persistenceUnit, EXCLUDE_UNLISTED_CLASSES);
320         if (excludeUnlistedClasses != null) {
321             unitInfo.setExcludeUnlistedClasses(true);
322         }
323
324         // mapping file
325
parseMappingFiles(persistenceUnit, unitInfo);
326         parseJarFiles(persistenceUnit, unitInfo);
327         parseClass(persistenceUnit, unitInfo);
328         parseProperty(persistenceUnit, unitInfo);
329         return unitInfo;
330     }
331
332     /**
333      * Parse the <code>property</code> XML elements.
334      */

335     @SuppressWarnings JavaDoc("unchecked")
336     protected void parseProperty(Element JavaDoc persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
337         Element JavaDoc propRoot = DomUtils.getChildElementByTagName(persistenceUnit, PROPERTIES);
338         if (propRoot == null) {
339             return;
340         }
341         List JavaDoc<Element JavaDoc> properties = DomUtils.getChildElementsByTagName(propRoot, "property");
342         for (Element JavaDoc property : properties) {
343             String JavaDoc name = property.getAttribute("name");
344             String JavaDoc value = property.getAttribute("value");
345             unitInfo.addProperty(name, value);
346         }
347     }
348
349     /**
350      * Parse the <code>class</code> XML elements.
351      */

352     @SuppressWarnings JavaDoc("unchecked")
353     protected void parseClass(Element JavaDoc persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
354         List JavaDoc<Element JavaDoc> classes = DomUtils.getChildElementsByTagName(persistenceUnit, MANAGED_CLASS_NAME);
355         for (Element JavaDoc element : classes) {
356             String JavaDoc value = DomUtils.getTextValue(element).trim();
357             if (StringUtils.hasText(value))
358                 unitInfo.addManagedClassName(value);
359         }
360     }
361
362     /**
363      * Parse the <code>jar-file</code> XML elements.
364      */

365     @SuppressWarnings JavaDoc("unchecked")
366     protected void parseJarFiles(Element JavaDoc persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException JavaDoc {
367         List JavaDoc<Element JavaDoc> jars = DomUtils.getChildElementsByTagName(persistenceUnit, JAR_FILE_URL);
368         for (Element JavaDoc element : jars) {
369             String JavaDoc value = DomUtils.getTextValue(element).trim();
370             if (StringUtils.hasText(value)) {
371                 Resource resource = this.resourcePatternResolver.getResource(value);
372                 unitInfo.addJarFileUrl(resource.getURL());
373             }
374         }
375     }
376
377     /**
378      * Parse the <code>mapping-file</code> XML elements.
379      */

380     @SuppressWarnings JavaDoc("unchecked")
381     protected void parseMappingFiles(Element JavaDoc persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
382         List JavaDoc<Element JavaDoc> files = DomUtils.getChildElementsByTagName(persistenceUnit, MAPPING_FILE_NAME);
383         for (Element JavaDoc element : files) {
384             String JavaDoc value = DomUtils.getTextValue(element).trim();
385             if (StringUtils.hasText(value))
386                 unitInfo.addMappingFileName(value);
387         }
388     }
389
390 }
391
Popular Tags