package org.apache.ojb.broker.metadata;
/* Copyright 2002-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.ojb.broker.util.ClassHelper;
import org.apache.ojb.broker.util.configuration.Configurable;
import org.apache.ojb.broker.util.configuration.Configuration;
import org.apache.ojb.broker.util.configuration.ConfigurationException;
import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
/**
* This class is responsible for reading and writing DescriptorRepository objects
* from and to persistent media.
* Currently only XML file based persistence is supported.
*
* @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
* @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
* @version $Id: RepositoryPersistor.java,v 1.24.2.4 2005/12/21 22:26:11 tomdz Exp $
*/
public class RepositoryPersistor implements Configurable
{
// TODO: Refactoring of the metadata reading/handling?
private static Logger log = LoggerFactory.getLogger(RepositoryPersistor.class);
private static final String SER_FILE_SUFFIX = "serialized";
private static final String SERIALIZED_REPOSITORY_PATH = "serializedRepositoryPath";
private boolean useSerializedRepository = false;
public RepositoryPersistor()
{
OjbConfigurator.getInstance().configure(this);
}
public void configure(Configuration pConfig) throws ConfigurationException
{
useSerializedRepository = ((MetadataConfiguration) pConfig).useSerializedRepository();
}
/**
* Write the {@link DescriptorRepository} to the given output object.
*/
public void writeToFile(DescriptorRepository repository, ConnectionRepository conRepository, OutputStream out)
{
RepositoryTags tags = RepositoryTags.getInstance();
try
{
if (log.isDebugEnabled())
log.debug("## Write repository file ##" +
repository.toXML() +
"## End of repository file ##");
String eol = SystemUtils.LINE_SEPARATOR;
StringBuffer buf = new StringBuffer();
// 1. write XML header
buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + eol);
buf.append("<!DOCTYPE descriptor-repository SYSTEM \"repository.dtd\" >" + eol + eol);
// strReturn += "<!DOCTYPE descriptor-repository SYSTEM \"repository.dtd\" [" + eol;
// strReturn += "<!ENTITY database-metadata SYSTEM \""+ConnectionRepository.DATABASE_METADATA_FILENAME+"\">" + eol;
// strReturn += "<!ENTITY user SYSTEM \"repository_user.xml\">" + eol;
// strReturn += "<!ENTITY junit SYSTEM \"repository_junit.xml\">" + eol;
// strReturn += "<!ENTITY internal SYSTEM \"repository_internal.xml\"> ]>" + eol + eol;
buf.append("<!-- OJB RepositoryPersistor generated this file on " + new Date().toString() + " -->" + eol);
buf.append(tags.getOpeningTagNonClosingById(RepositoryElements.MAPPING_REPOSITORY) + eol);
buf.append(" " + tags.getAttribute(RepositoryElements.REPOSITORY_VERSION, DescriptorRepository.getVersion()) + eol);
buf.append(" " + tags.getAttribute(RepositoryElements.ISOLATION_LEVEL, repository.getIsolationLevelAsString()) + eol);
buf.append(">" + eol);
if(conRepository != null) buf.append(eol + eol + conRepository.toXML() + eol + eol);
if(repository != null) buf.append(repository.toXML());
buf.append(tags.getClosingTagById(RepositoryElements.MAPPING_REPOSITORY));
PrintWriter pw = new PrintWriter(out);
pw.print(buf.toString());
pw.flush();
pw.close();
}
catch (Exception e)
{
log.error("Could not write to output stream" + out, e);
}
}
/**
* Read the repository configuration file.
* <br>
* If configuration property <code>useSerializedRepository</code> is <code>true</code>
* all subsequent calls read a serialized version of the repository.
* The directory where the serialized repository is stored can be specified
* with the <code>serializedRepositoryPath</code> entry in OJB.properties.
* Once a serialized repository is found changes to repository.xml will be
* ignored. To force consideration of these changes the serialized repository
* must be deleted manually.
*/
public DescriptorRepository readDescriptorRepository(String filename)
throws MalformedURLException, ParserConfigurationException, SAXException, IOException
{
DescriptorRepository result;
if (useSerializedRepository)
// use serialized repository
{
// build File object pointing to serialized repository location
Configuration config = OjbConfigurator.getInstance().getConfigurationFor(null);
String pathPrefix = config.getString(SERIALIZED_REPOSITORY_PATH, ".");
File serFile = new File(pathPrefix + File.separator + filename + "." + SER_FILE_SUFFIX);
if (serFile.exists() && serFile.length() > 0)
// if file exists load serialized version of repository
{
try
{
long duration = System.currentTimeMillis();
result = deserialize(serFile);
log.info("Read serialized repository in " + (System.currentTimeMillis() - duration) + " ms");
}
catch (Exception e)
{
log.error("error in loading serialized repository. Will try to use XML version.", e);
result = (DescriptorRepository) buildRepository(filename, DescriptorRepository.class);
}
}
else
// if no serialized version exists, read it from xml and write serialized file
{
long duration = System.currentTimeMillis();
result = (DescriptorRepository) buildRepository(filename, DescriptorRepository.class);
log.info("Read repository from file took " + (System.currentTimeMillis() - duration) + " ms");
serialize(result, serFile);
}
}
// don't use serialized repository
else
{
long duration = System.currentTimeMillis();
result = (DescriptorRepository) buildRepository(filename, DescriptorRepository.class);
log.info("Read class descriptors took " + (System.currentTimeMillis() - duration) + " ms");
}
return result;
}
public DescriptorRepository readDescriptorRepository(InputStream inst)
throws MalformedURLException, ParserConfigurationException, SAXException, IOException
{
long duration = System.currentTimeMillis();
InputSource inSource = new InputSource(inst);
DescriptorRepository result = (DescriptorRepository) readMetadataFromXML(inSource, DescriptorRepository.class);
log.info("Read class descriptors took " + (System.currentTimeMillis() - duration) + " ms");
return result;
}
/**
* Read the repository configuration file and extract connection handling information.
*/
public ConnectionRepository readConnectionRepository(String filename)
throws MalformedURLException, ParserConfigurationException, SAXException, IOException
{
long duration = System.currentTimeMillis();
ConnectionRepository result = (ConnectionRepository) buildRepository(filename, ConnectionRepository.class);
log.info("Read connection repository took " + (System.currentTimeMillis() - duration) + " ms");
return result;
}
/**
* Read the repository configuration file and extract connection handling information.
*/
public ConnectionRepository readConnectionRepository(InputStream inst)
throws MalformedURLException, ParserConfigurationException, SAXException, IOException
{
long duration = System.currentTimeMillis();
InputSource inSource = new InputSource(inst);
ConnectionRepository result = (ConnectionRepository) readMetadataFromXML(inSource, ConnectionRepository.class);
log.info("Read connection repository took " + (System.currentTimeMillis() - duration) + " ms");
return result;
}
protected DescriptorRepository deserialize(File serFile)
{
DescriptorRepository result = null;
try
{
FileInputStream fis = new FileInputStream(serFile);
// deserialize repository
result = (DescriptorRepository) SerializationUtils.deserialize(fis);
}
catch (Exception e)
{
log.error("Deserialisation failed, using input path: " + serFile.getAbsolutePath(), e);
}
return result;
}
protected void serialize(DescriptorRepository repository, File file)
{
try
{
FileOutputStream fos = new FileOutputStream(file);
// serialize repository
SerializationUtils.serialize(repository, fos);
}
catch (Exception e)
{
log.error("Serialization failed, using output path: " + file.getAbsolutePath(), e);
}
}
/**
*
* TODO: We should re-design the configuration file reading
*/
private Object buildRepository(String repositoryFileName, Class targetRepository)
throws MalformedURLException, ParserConfigurationException, SAXException, IOException
{
URL url = buildURL(repositoryFileName);
/*
arminw:
strange, when using 'url.openStream()' argument repository
could not be parsed
ipriha:
parser needs a base url to find referenced entities.
*/
// InputSource source = new InputSource(url.openStream());
String pathName = url.toExternalForm();
log.info("Building repository from :" + pathName);
InputSource source = new InputSource(pathName);
URLConnection conn = url.openConnection();
conn.setUseCaches(false);
conn.connect();
InputStream in = conn.getInputStream();
source.setByteStream(in);
try
{
return readMetadataFromXML(source, targetRepository);
}
finally
{
try
{
in.close();
}
catch (IOException x)
{
log.warn("unable to close repository input stream [" + x.getMessage() + "]", x);
}
}
}
/**
* Read metadata by populating an instance of the target class
* using SAXParser.
*/
private Object readMetadataFromXML(InputSource source, Class target)
throws MalformedURLException, ParserConfigurationException, SAXException, IOException
{
// TODO: make this configurable
boolean validate = false;
// get a xml reader instance:
SAXParserFactory factory = SAXParserFactory.newInstance();
log.info("RepositoryPersistor using SAXParserFactory : " + factory.getClass().getName());
if (validate)
{
factory.setValidating(true);
}
SAXParser p = factory.newSAXParser();
XMLReader reader = p.getXMLReader();
if (validate)
{
reader.setErrorHandler(new OJBErrorHandler());
}
Object result;
if (DescriptorRepository.class.equals(target))
{
// create an empty repository:
DescriptorRepository repository = new DescriptorRepository();
// create handler for building the repository structure
ContentHandler handler = new RepositoryXmlHandler(repository);
// tell parser to use our handler:
reader.setContentHandler(handler);
reader.parse(source);
result = repository;
}
else if (ConnectionRepository.class.equals(target))
{
// create an empty repository:
ConnectionRepository repository = new ConnectionRepository();
// create handler for building the repository structure
ContentHandler handler = new ConnectionDescriptorXmlHandler(repository);
// tell parser to use our handler:
reader.setContentHandler(handler);
reader.parse(source);
//LoggerFactory.getBootLogger().info("loading XML took " + (stop - start) + " msecs");
result = repository;
}
else
throw new MetadataException("Could not build a repository instance for '" + target +
"', using source " + source);
return result;
}
private URL buildURL(String repositoryFileName) throws MalformedURLException
{
//j2ee compliant lookup of resources
URL url = ClassHelper.getResource(repositoryFileName);
// don't be too strict: if resource is not on the classpath, try ordinary file lookup
if (url == null)
{
try
{
url = new File(repositoryFileName).toURL();
}
catch (MalformedURLException ignore)
{
}
}
if (url != null)
{
log.info("OJB Descriptor Repository: " + url);
}
else
{
throw new MalformedURLException("did not find resource " + repositoryFileName);
}
return url;
}
// inner class
class OJBErrorHandler implements ErrorHandler
{
public void warning(SAXParseException exception)
throws SAXException
{
logMessage(exception, false);
}
public void error(SAXParseException exception)
throws SAXException
{
logMessage(exception, false);
}
public void fatalError(SAXParseException exception)
throws SAXException
{
logMessage(exception, true);
}
void logMessage(SAXParseException e, boolean isFatal)
{
String msg = e.getMessage();
if (isFatal)
{
log.error("## " + e.getSystemId() + " - line " + e.getLineNumber() + ": " + msg + " ##");
}
else
{
log.warn(e.getSystemId() + " - line " + e.getLineNumber() + ": " + msg);
}
}
}
}