/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.jpa.puparser;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.jboss.as.jpa.config.Configuration;
import org.jboss.as.jpa.config.PersistenceUnitMetadataHolder;
import org.jboss.as.jpa.config.PersistenceUnitMetadataImpl;
import org.jboss.metadata.parser.util.MetaDataElementParser;
import org.jboss.metadata.property.PropertyReplacer;
import org.jipijapa.plugin.spi.PersistenceUnitMetadata;
import static org.jboss.as.jpa.messages.JpaLogger.ROOT_LOGGER;
/**
* Parse a persistence.xml into a list of persistence unit definitions.
*
* @author Scott Marlow
*/
public class PersistenceUnitXmlParser extends MetaDataElementParser {
// cache the trace enabled flag
private static final boolean traceEnabled = ROOT_LOGGER.isTraceEnabled();
public static PersistenceUnitMetadataHolder parse(final XMLStreamReader reader, final PropertyReplacer propertyReplacer) throws XMLStreamException {
reader.require(START_DOCUMENT, null, null); // check for a bogus document and throw error
// Read until the first start element
Version version = null;
while (reader.hasNext() && reader.next() != START_ELEMENT) {
if (reader.getEventType() == DTD) {
final String dtdLocation = readDTDLocation(reader);
if (dtdLocation != null) {
version = Version.forLocation(dtdLocation);
}
}
}
final String schemaLocation = readSchemaLocation(reader);
if (schemaLocation != null) {
version = Version.forLocation(schemaLocation);
}
if (version == null || Version.UNKNOWN.equals(version)) {
// Look at the version attribute
String versionString = null;
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i++) {
final String attributeNamespace = reader.getAttributeNamespace(i);
if (attributeNamespace != null && !attributeNamespace.isEmpty()) {
continue;
}
final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
if (attribute == Attribute.VERSION) {
versionString = reader.getAttributeValue(i);
}
}
if ("1.0".equals(versionString)) {
version = Version.JPA_1_0;
} else if ("1".equals(versionString)) {
version = Version.JPA_1_0;
} else if ("2.0".equals(versionString)) {
version = Version.JPA_2_0;
} else if ("2.1".equals(versionString)) {
version = Version.JPA_2_1;
} else if ("2".equals(versionString)) {
version = Version.JPA_2_0;
} else {
version = Version.JPA_2_1;
}
}
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i++) {
final String attributeNamespace = reader.getAttributeNamespace(i);
if (attributeNamespace != null && !attributeNamespace.isEmpty()) {
continue;
}
final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
switch (attribute) {
case VERSION:
// log.info("version = " + value);
// TODO: handle version
break;
default:
throw unexpectedAttribute(reader, i);
}
}
final List<PersistenceUnitMetadata> PUs = new ArrayList<PersistenceUnitMetadata>();
// until the ending PERSISTENCE tag
while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case PERSISTENCEUNIT:
PersistenceUnitMetadata pu = parsePU(reader, version, propertyReplacer);
PUs.add(pu);
ROOT_LOGGER.readingPersistenceXml(pu.getPersistenceUnitName());
break;
default:
throw unexpectedElement(reader);
}
}
PersistenceUnitMetadataHolder result = new PersistenceUnitMetadataHolder(PUs);
if (ROOT_LOGGER.isTraceEnabled())
ROOT_LOGGER.trace(result.toString());
return result;
}
/**
* Parse the persistence unit definitions based on persistence_2_0.xsd.
*
*
* @param reader
* @param propertyReplacer
* @return
* @throws XMLStreamException
*/
private static PersistenceUnitMetadata parsePU(XMLStreamReader reader, Version version, final PropertyReplacer propertyReplacer) throws XMLStreamException {
PersistenceUnitMetadata pu = new PersistenceUnitMetadataImpl();
List<String> classes = new ArrayList<String>(1);
List<String> jarFiles = new ArrayList<String>(1);
List<String> mappingFiles = new ArrayList<String>(1);
Properties properties = new Properties();
// set defaults
pu.setTransactionType(PersistenceUnitTransactionType.JTA);
pu.setValidationMode(ValidationMode.AUTO);
pu.setSharedCacheMode(SharedCacheMode.UNSPECIFIED);
pu.setPersistenceProviderClassName(Configuration.PROVIDER_CLASS_DEFAULT);
if (version.equals(Version.JPA_1_0)) {
pu.setPersistenceXMLSchemaVersion("1.0");
} else if (version.equals(Version.JPA_2_0)) {
pu.setPersistenceXMLSchemaVersion("2.0");
} else {
pu.setPersistenceXMLSchemaVersion("2.1");
}
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i++) {
final String value = reader.getAttributeValue(i);
if (traceEnabled) {
ROOT_LOGGER.tracef("parse persistence.xml: attribute value(%d) = %s", i, value);
}
final String attributeNamespace = reader.getAttributeNamespace(i);
if (attributeNamespace != null && !attributeNamespace.isEmpty()) {
continue;
}
final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
switch (attribute) {
case NAME:
pu.setPersistenceUnitName(value);
break;
case TRANSACTIONTYPE:
if (value.equalsIgnoreCase("RESOURCE_LOCAL"))
pu.setTransactionType(PersistenceUnitTransactionType.RESOURCE_LOCAL);
break;
default:
throw unexpectedAttribute(reader, i);
}
}
// until the ending PERSISTENCEUNIT tag
while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
if (traceEnabled) {
ROOT_LOGGER.tracef("parse persistence.xml: element=%s", element.getLocalName());
}
switch (element) {
case CLASS:
classes.add(getElement(reader, propertyReplacer));
break;
case DESCRIPTION:
final String description = getElement(reader, propertyReplacer);
break;
case EXCLUDEUNLISTEDCLASSES:
String text = getElement(reader, propertyReplacer);
if (text == null || text.isEmpty()) {
//the spec has examples where an empty
//exclude-unlisted-classes element has the same
//effect as setting it to true
pu.setExcludeUnlistedClasses(true);
} else {
pu.setExcludeUnlistedClasses(Boolean.valueOf(text));
}
break;
case JARFILE:
String file = getElement(reader, propertyReplacer);
jarFiles.add(file);
break;
case JTADATASOURCE:
pu.setJtaDataSourceName(getElement(reader, propertyReplacer));
break;
case NONJTADATASOURCE:
pu.setNonJtaDataSourceName(getElement(reader, propertyReplacer));
break;
case MAPPINGFILE:
mappingFiles.add(getElement(reader, propertyReplacer));
break;
case PROPERTIES:
parseProperties(reader, properties, propertyReplacer);
break;
case PROVIDER:
pu.setPersistenceProviderClassName(getElement(reader, propertyReplacer));
break;
case SHAREDCACHEMODE:
String cm = getElement(reader, propertyReplacer);
pu.setSharedCacheMode(SharedCacheMode.valueOf(cm));
break;
case VALIDATIONMODE:
String validationMode = getElement(reader, propertyReplacer);
pu.setValidationMode(ValidationMode.valueOf(validationMode));
break;
default:
throw unexpectedElement(reader);
}
}
if (traceEnabled) {
ROOT_LOGGER.trace("parse persistence.xml: reached ending persistence-unit tag");
}
pu.setManagedClassNames(classes);
pu.setJarFiles(jarFiles);
pu.setMappingFiles(mappingFiles);
pu.setProperties(properties);
return pu;
}
private static void parseProperties(XMLStreamReader reader, Properties properties, final PropertyReplacer propertyReplacer) throws XMLStreamException {
while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
final Element element = Element.forName(reader.getLocalName());
switch (element) {
case PROPERTY:
final int count = reader.getAttributeCount();
String name = null, value = null;
for (int i = 0; i < count; i++) {
final String attributeValue = getAttribute(reader, i, propertyReplacer);
final String attributeNamespace = reader.getAttributeNamespace(i);
if (attributeNamespace != null && !attributeNamespace.isEmpty()) {
continue;
}
final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
switch (attribute) {
case NAME:
name = attributeValue;
break;
case VALUE:
value = attributeValue;
if (name != null && value != null) {
properties.put(name, value);
}
name = value = null;
break;
default:
throw unexpectedAttribute(reader, i);
}
}
if (reader.hasNext() && (reader.nextTag() != END_ELEMENT))
throw unexpectedElement(reader);
break;
default:
throw unexpectedElement(reader);
}
}
}
private static String getElement(final XMLStreamReader reader, final PropertyReplacer propertyReplacer) throws XMLStreamException {
return propertyReplacer.replaceProperties(reader.getElementText());
}
private static String getAttribute(final XMLStreamReader reader, int i, final PropertyReplacer propertyReplacer) throws XMLStreamException {
return propertyReplacer.replaceProperties(reader.getAttributeValue(i));
}
}