/*
* BEEN: Benchmarking Environment
* ==============================
*
* File author: Andrej Podzimek
*
* GNU Lesser General Public License Version 2.1
* ---------------------------------------------
* Copyright (C) 2004-2006 Distributed Systems Research Group,
* Faculty of Mathematics and Physics, Charles University in Prague
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package cz.cuni.mff.d3s.been.core.jaxb;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.SAXException;
/**
* A simple XSD files list to avoid magic String constants in the code.
*
* @author Andrej Podzimek
*/
public enum XSD {
RUNTIME("http://been.d3s.mff.cuni.cz/runtime-info", XSDFile.COMMON.FILE,
XSDFile.HARDWARE_INFO.FILE, XSDFile.RUNTIME.FILE),
TASKENTRY("http://been.d3s.mff.cuni.cz/task-entry", XSDFile.COMMON.FILE,
XSDFile.TASK_DESCRIPTOR.FILE, XSDFile.TASKENTRY.FILE),
TASK_DESCRIPTOR("http://been.d3s.mff.cuni.cz/task-descriptor",
XSDFile.COMMON.FILE, XSDFile.TASK_DESCRIPTOR.FILE),
TASK_CONTEXT_DESCRIPTOR(
"http://been.d3s.mff.cuni.cz/task-context-descriptor",
XSDFile.COMMON.FILE, XSDFile.TASK_DESCRIPTOR.FILE,
XSDFile.TASK_CONTEXT_DESCRIPTOR.FILE),
STORAGE("http://been.d3s.mff.cuni.cz/task-context-descriptor", XSDFile.BENCHMARK_ENTRY.FILE);
/**
* A simple class that holds a list of XSD files and can initialize the XML
* Schema on demand.
*
* @author Andrej Podzimek
*/
private abstract class FilesContainer {
/** Array of XSD files required by the Schema. */
private File[] files;
/**
* Initializes a new container with fies.
*
* @param files
* Array of XSD files that can be used to produce a Schema.
*/
FilesContainer(File[] files) {
this.files = files;
}
/**
* Creates a new schema when necessary. As for thread safety, this is OK.
* The {@code createSchema()} method is (artificially) thread-safe. The
* worst thing that can happen are two consecutive assignments to schema. N
*
* @throws org.xml.sax.SAXException
* When the low level SAX parser fails.
*/
void initializeSchema() throws SAXException {
synchronized (factory) { // Artificial thread safety.
if (null == schema) {
StreamSource[] sources;
sources = new StreamSource[files.length];
for (int i = 0; i < files.length; ++i) {
InputStream input = XSD.class.getClassLoader().getResourceAsStream("xsd/" + files[i].getName());
assert input != null;
sources[i] = new StreamSource(input);
}
schema = factory.newSchema(sources);
files = null; // Can be GCd despite 2nd FTXPF.
}
}
}
}
/**
* A special factory class that handles the first invocation and initializes
* the enum member. Members can't be initialized statically, for that would
* require the XSD files to be omnipresent. With dynamic initialization, we
* can only include the files that are needed.
*
* @author Andrej Podzimek
*/
private class FirstTimeXMLParserFactory extends FilesContainer implements XMLParserFactory {
/**
* Initializes the new factory with the supplied bunch of XSD files.
*
* @param files
* XSD files that can be used to produce a XML Schema.
*/
public FirstTimeXMLParserFactory(File[] files) {
super(files);
}
@Override
public <T extends Serializable> BindingParser<T> internalCreateParser(
Class<T> bindingClass) throws SAXException, JAXBException {
synchronized (XSD.this) { // Concurrent first calls.
if (null == parserContexts) { // Not initialized yet.
initializeSchema();
parserContexts = new HashMap<Class<? extends Serializable>, JAXBContext>();
parserExecutor = new NextTimeXMLParserFactory();
}
}
return createParser(bindingClass);
}
}
/**
* A special factory instance that handles the first invocation and
* initializes the enum member. Members can't be initialized statically, for
* that would require the XSD files to be omnipresent. With dynamic
* initialization, we can only include the files that are needed.
*
* @author Andrej Podzimek
*/
private class FirstTimeXMLComposerFactory extends FilesContainer implements XMLComposerFactory {
/**
* Initializes the new factory with the supplied bunch of XSD files.
*
* @param files
* XSD files that can be used to produce a XML Schema.
*/
public FirstTimeXMLComposerFactory(File[] files) {
super(files);
}
@Override
public <T extends Serializable> BindingComposer<T> internalCreateComposer(
Class<T> bindingClass) throws SAXException, JAXBException {
initialize();
return createComposer(bindingClass);
}
/**
* Initializes all the basic fields of the enclosing enum member.
*
* @throws org.xml.sax.SAXException
* When the low level SAX parser fails.
*/
private void initialize() throws SAXException {
synchronized (XSD.this) { // Concurrent first calls.
if (null == composerContexts) { // Not initialized yet.
initializeSchema();
composerContexts = new HashMap<Class<? extends Serializable>, JAXBContext>();
composerExecutor = new NextTimeXMLComposerFactory();
}
}
}
}
/**
* A factory class that handles all the invocations except the first one.
*
* @author Andrej Podzimek
*/
private class NextTimeXMLParserFactory implements XMLParserFactory {
@Override
public <T extends Serializable> BindingParser<T> internalCreateParser(
Class<T> bindingClass) throws JAXBException {
JAXBContext context;
synchronized (parserContexts) { // Concurrent requests.
context = parserContexts.get(bindingClass);
if (null == context) {
context = JAXBContext.newInstance(bindingClass);
parserContexts.put(bindingClass, context);
}
}
return new XMLParser<T>(context, schema);
}
}
/**
* A factory class that handles all the invocations except the first one.
*
* @author Andrej Podzimek
*/
private class NextTimeXMLComposerFactory implements XMLComposerFactory {
@Override
public <T extends Serializable> BindingComposer<T> internalCreateComposer(
Class<T> bindingClass) throws JAXBException {
JAXBContext context;
synchronized (composerContexts) { // Concurrent requests.
context = composerContexts.get(bindingClass);
if (null == context) {
context = JAXBContext.newInstance(bindingClass);
composerContexts.put(bindingClass, context);
}
}
return new XMLComposer<T>(context, schema);
}
}
/** A factory that can produce XML schemas. */
private static final SchemaFactory factory;
static {
factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // OK, this is serialized.
}
/** The namespace URI. */
public final String URI;
/** The XML schema to use for marshaller/unmarshaller initialization. */
private Schema schema;
/** The contexts used to create unmarshallers. */
private HashMap<Class<? extends Serializable>, JAXBContext> parserContexts;
/** The contexts used to create marshallers. */
private HashMap<Class<? extends Serializable>, JAXBContext> composerContexts;
/**
* The instance that actually answers factory requests. First request handled
* differently.
*/
private XMLParserFactory parserExecutor;
/**
* The instance that actually answers factory requests. First request handled
* differently.
*/
private XMLComposerFactory composerExecutor;
/**
* Thi senum member initializer sets the {@code URI} and {@code PREFIX}
* constants, initializes the list of XSD files and creates special parser
* executors that will finish the initialization dynamically at first
* invocation.
*
* @param uri
* URI of the namespace to initialize.
* @param files
* An array of files that for a XML Schema together.
*/
private XSD(String uri, File... files) {
this.URI = uri;
this.parserExecutor = new FirstTimeXMLParserFactory(files);
this.composerExecutor = new FirstTimeXMLComposerFactory(files);
}
/**
* Creates an XML composer.
*
* @param <T>
* Type of the binding class.
* @param bindingClass
* The binding class.
* @return An XML composer bound to the requested class.
* @throws org.xml.sax.SAXException
* When a low-level SAX parser failure occurs.
* @throws javax.xml.bind.JAXBException
* When the JAXB class is refused.
*/
public <T extends Serializable> BindingComposer<T> createComposer(
Class<T> bindingClass) throws SAXException, JAXBException {
return composerExecutor.internalCreateComposer(bindingClass);
}
/**
* Creates an XML parser.
*
* @param <T>
* Type of the binding class.
* @param bindingClass
* The binding class.
* @return An XML parser bound to the requested class.
* @throws org.xml.sax.SAXException
* When a low-level SAX parser failure occurs.
* @throws javax.xml.bind.JAXBException
* When the JAXB class is refused.
*/
public <T extends Serializable> BindingParser<T> createParser(
Class<T> bindingClass) throws SAXException, JAXBException {
return parserExecutor.internalCreateParser(bindingClass);
}
/**
* A utility method for creating a Schema from a source unknown to the XSD
* enum.
*
* @param sources
* The XSD schema data.
* @return A new XML schema.
* @throws org.xml.sax.SAXException
* When a low-level SAX parser failure occurs.
*/
public static Schema createSchema(Source... sources) throws SAXException {
synchronized (factory) {
return factory.newSchema(sources);
}
}
}