/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt 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.jbpm.wire.binding;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;
import org.hibernate.cfg.Configuration;
import org.jbpm.PvmException;
import org.jbpm.wire.WireContext;
import org.jbpm.wire.descriptor.HibernateConfigurationDescriptor;
import org.jbpm.wire.descriptor.PropertiesDescriptor;
import org.jbpm.wire.operation.Operation;
import org.jbpm.xml.Binding;
import org.jbpm.xml.Parse;
import org.jbpm.xml.Parser;
import org.jbpm.xml.XmlUtil;
import org.w3c.dom.Element;
/** builds a hibernate configuration.
*
* <p>Attribute <b><code>class</code></b> is optional and the default is set to
* <code>org.hibernate.cfg.Configuration</code>. Alternative configuration class names could be
* <code>org.hibernate.cfg.AnnotationConfiguration</code> or an <code>org.hibernate.ejb3.Ejb3Configuration</code>.
* </p>
*
* <p><b><code>mapping</code></b> elements can refer to mapping resource files by means of the following attributes:
* <ul>
* <li>Attribute <b><code>resource</code></b></li>
* <li>Attribute <b><code>file</code></b></li>
* <li>Attribute <b><code>class</code></b></li>
* <li>Attribute <b><code>url</code></b></li>
* </ul>
* </p>
*
* <p>A <b><code>properties</code></b> element can specify hibernate configuration properties like
* specified in {@link PropertiesBinding}.
* </p>
*
* @author Tom Baeyens
*/
public class HibernateConfigurationBinding implements Binding {
private static final Logger log = Logger.getLogger(HibernateConfigurationBinding.class.getName());
private static final PropertiesBinding propertiesBinding = new PropertiesBinding();
private static final MappingParser mappingParser = new MappingParser();
static class MappingParser extends Parser {
public Object parseDocumentElement(Element documentElement, Parse parse) {
List<Element> elements = XmlUtil.elements(documentElement, "mapping");
if (elements!=null) {
HibernateConfigurationDescriptor descriptor = parse.findObject(HibernateConfigurationDescriptor.class);
for (Element element: elements) {
parseMapping(element, descriptor, parse);
}
}
return null;
}
}
static void parseMapping(Element element, HibernateConfigurationDescriptor descriptor, Parse parse) {
if (element.hasAttribute("resource")) {
descriptor.addMappingOperation(new AddResource(element.getAttribute("resource")));
} else if (element.hasAttribute("file")) {
descriptor.addMappingOperation(new AddFile(element.getAttribute("file")));
} else if (element.hasAttribute("class")) {
descriptor.addMappingOperation(new AddClass(element.getAttribute("class")));
} else if (element.hasAttribute("url")) {
descriptor.addMappingOperation(new AddUrl(element.getAttribute("url")));
} else {
parse.addProblem("exactly 1 attribute in {resource, file, class, url} was expected in mapping: "+XmlUtil.toString(element));
}
}
public Object parse(Element element, Parse parse, Parser parser) {
HibernateConfigurationDescriptor descriptor = new HibernateConfigurationDescriptor();
String configurationClassName = null;
if (element.hasAttribute("class")) {
configurationClassName = element.getAttribute("class");
} else {
configurationClassName = Configuration.class.getName();
}
descriptor.setClassName(configurationClassName);
if (element.hasAttribute("table-prefix")) {
descriptor.setTablePrefix(element.getAttribute("table-prefix"));
}
if (element.hasAttribute("naming-strategy")) {
descriptor.setNamingStrategyClassName(element.getAttribute("naming-strategy"));
}
// TODO add schema-update="enabled" attibute that automatically applies the schema generation
List<Element> configElements = XmlUtil.elements(element);
if (configElements!=null) {
for (Element configElement: configElements) {
if ("mappings".equals(XmlUtil.getTagName(configElement))) {
if (configElement.hasAttribute("resource")) {
String resource = configElement.getAttribute("resource");
InputStream stream = parser.getClassLoader().getResourceAsStream(resource);
parser.importStream(stream, configElement, parse);
}
if (configElement.hasAttribute("resources")) {
String resources = configElement.getAttribute("resources");
try {
Enumeration<URL> enumeration = parser.getClassLoader().getResources(resources);
if (enumeration!=null) {
while (enumeration.hasMoreElements()) {
URL url = enumeration.nextElement();
log.finest("parsing mappings resource from "+url);
InputStream inputStream = url.openStream();
parse.pushObject(descriptor);
try {
mappingParser.parseInputStream(inputStream, parse);
} finally {
parse.popObject();
inputStream.close();
}
}
}
} catch (Exception e) {
parse.addProblem("couldn't parse hibernate mapping resources '"+resources+"'", e);
}
}
} else if ("mapping".equals(XmlUtil.getTagName(configElement))) {
parseMapping(configElement, descriptor, parse);
} else if ("properties".equals(XmlUtil.getTagName(configElement))) {
PropertiesDescriptor propertiesDescriptor = (PropertiesDescriptor) propertiesBinding.parse(configElement, parse, parser);
descriptor.setPropertiesDescriptor(propertiesDescriptor);
} else if ("cache-configuration".equals(XmlUtil.getTagName(configElement))) {
InputStream stream = null;
String cacheUsage = configElement.getAttribute("usage");
if (! ( ("read-only".equals(cacheUsage))
|| ("nonstrict-read-write".equals(cacheUsage))
|| ("read-write".equals(cacheUsage))
|| ("transactional".equals(cacheUsage))
)
){
parse.addProblem("problem in cache-configuration: no usage attribute or illegal value: "+cacheUsage+" Possible values are {read-only, nonstrict-read-write, read-write, transactional}");
} else {
if (configElement.hasAttribute("file")) {
String fileName = configElement.getAttribute("file");
File file = new File(fileName);
if (file.exists() && file.isFile()) {
try {
stream = new FileInputStream(file);
} catch (FileNotFoundException e) {
parse.addProblem("file "+fileName+" isn't a file", e);
}
} else {
parse.addProblem("file "+fileName+" isn't a file");
}
}
if (configElement.hasAttribute("resource")) {
String resource = configElement.getAttribute("resource");
stream = parser.getClassLoader().getResourceAsStream(resource);
if (stream==null) {
parse.addProblem("resource "+resource+" doesn't exist");
}
}
if (configElement.hasAttribute("url")) {
String urlText = configElement.getAttribute("url");
try {
URL url = new URL(urlText);
stream = url.openStream();
if (stream==null) {
parse.addProblem("couldn't open url "+urlText);
}
} catch (Exception e) {
parse.addProblem("couldn't open url "+urlText, e);
}
}
if (stream != null) {
parser.importStream(stream, configElement, parse);
}
// parse the cache configurations in the same way as the hibernate cfg schema
// translate the contents of the file into invoke operations for methods
// Configuration.setCacheConcurrencyStrategy(String clazz, String concurrencyStrategy, String region)
// Configuration.setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy)
// <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
// <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
// <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>
List<Element> cacheElements = XmlUtil.elements(configElement);
if (cacheElements!=null) {
for (Element cacheElement : cacheElements) {
if ("class-cache".equals(XmlUtil.getTagName(cacheElement))) {
String className = cacheElement.getAttribute("class");
descriptor.addCacheOperation(new SetCacheConcurrencyStrategy(className, cacheUsage));
} else if ("collection-cache".equals(XmlUtil.getTagName(cacheElement))) {
String collection = cacheElement.getAttribute("collection");
descriptor.addCacheOperation(new SetCollectionCacheConcurrencyStrategy(collection, cacheUsage));
} else {
parse.addProblem("unknown hibernate cache configuration element "+XmlUtil.toString(configElement));
}
}
}
}
} else {
parse.addProblem("unknown hibernate configuration element "+XmlUtil.toString(configElement));
}
}
}
return descriptor;
}
public static class AddResource implements Operation {
private static final long serialVersionUID = 1L;
String resource;
public AddResource(String resource) {
this.resource = resource;
}
public void apply(Object target, WireContext wireContext) {
Configuration configuration = (Configuration) target;
configuration.addResource(resource, wireContext.getClassLoader());
}
public String toString() {
return "adding mapping resource "+resource+" to hibernate configuration";
}
}
public static class AddFile implements Operation {
private static final long serialVersionUID = 1L;
String fileName;
public AddFile(String fileName) {
this.fileName = fileName;
}
public void apply(Object target, WireContext wireContext) {
Configuration configuration = (Configuration) target;
configuration.addFile(fileName);
}
public String toString() {
return "adding hibernate mapping file "+fileName+" to configuration";
}
}
public static class AddClass implements Operation {
private static final long serialVersionUID = 1L;
String className;
public AddClass(String className) {
this.className = className;
}
public void apply(Object target, WireContext wireContext) {
Configuration configuration = (Configuration) target;
try {
Class<?> persistentClass = wireContext.getClassLoader().loadClass(className);
configuration.addClass(persistentClass);
} catch (Exception e) {
throw new PvmException("couldn't add mapping for class "+className, e);
}
}
public String toString() {
return "adding persistent class "+className+" to hibernate configuration";
}
}
public static class AddUrl implements Operation {
private static final long serialVersionUID = 1L;
String url;
public AddUrl(String url) {
this.url = url;
}
public void apply(Object target, WireContext wireContext) {
Configuration configuration = (Configuration) target;
try {
configuration.addURL(new URL(url));
} catch (Exception e) {
throw new PvmException("couldn't add hibernate mapping from URL "+url, e);
}
}
}
public static class SetCacheConcurrencyStrategy implements Operation {
private static final long serialVersionUID = 1L;
String className;
String concurrencyStrategy;
public SetCacheConcurrencyStrategy(String className, String concurrencyStrategy) {
this.className = className;
this.concurrencyStrategy = concurrencyStrategy;
}
public void apply(Object target, WireContext wireContext) {
Configuration configuration = (Configuration) target;
configuration.setCacheConcurrencyStrategy(className, concurrencyStrategy);
}
public String toString() {
return "setting cache concurrency strategy on class "+className+" to "+concurrencyStrategy+" on hibernate configuration";
}
}
public static class SetCollectionCacheConcurrencyStrategy implements Operation {
private static final long serialVersionUID = 1L;
String collection;
String concurrencyStrategy;
public SetCollectionCacheConcurrencyStrategy(String collection, String concurrencyStrategy) {
this.collection = collection;
this.concurrencyStrategy = concurrencyStrategy;
}
public void apply(Object target, WireContext wireContext) {
Configuration configuration = (Configuration) target;
configuration.setCollectionCacheConcurrencyStrategy(collection, concurrencyStrategy);
}
public String toString() {
return "setting cache concurrency strategy on collection "+collection+" to "+concurrencyStrategy+" on hibernate configuration";
}
}
}