package org.apache.ojb.broker.metadata;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache ObjectRelationalBridge" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache ObjectRelationalBridge", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.ojb.broker.PersistenceBrokerFactory;
import org.apache.ojb.broker.util.configuration.ConfigurationException;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.jdo.metadata.OjbJdoXmlHandler;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * 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>
 * @version $Id: RepositoryPersistor.java,v 1.4 2002/09/21 15:49:37 brj Exp $
 */
public class RepositoryPersistor
{
    public void writeToFile(DescriptorRepository repository, String filename)
    {
        String result = repository.toXML();
        //System.out.print(result);
        try
        {
            FileOutputStream fos = new FileOutputStream(filename);
            PrintWriter pw = new PrintWriter(fos);
            pw.print(result);
            pw.flush();
            pw.close();
        }
        catch (Throwable t)
        {
            LoggerFactory.getDefaultLogger().fatal(
                "Could not write to file" + filename,
                t);
        }
    }

    public DescriptorRepository readFromFile(String filename)
        throws
            MalformedURLException,
            ParserConfigurationException,
            SAXException,
            IOException
    {
        /** JDO branch added by Travis */
        if (filename.endsWith(".jdo"))
        {
            return buildRepositoryFromJdo(filename);

        }

        boolean useSerializedFile = false;
        try
        {
            useSerializedFile =
                PersistenceBrokerFactory.getConfigurator().getConfigurationFor(
                    null).getBoolean(
                    "useSerializedRepository",
                    false);
        }
        catch (ConfigurationException ex)
        {
            // may be savely ignored
        }

        File serFile = new File(filename + ".serialized");
        if (useSerializedFile && serFile.exists())
        {
            try
            {
                DescriptorRepository result = deserialize(serFile);
                return result;
            }
            catch (Throwable t)
            {
                LoggerFactory.getBootLogger().error(
                    "error in loading serialized repository. Will try to use XML version.",
                    t);
                return buildRepository(filename);
            }

        }
        else
        {
            DescriptorRepository result = buildRepository(filename);
            if (useSerializedFile)
            {
                serialize(result, serFile);
            }
            return result;
        }
    }

    private DescriptorRepository deserialize(File serFile)
        throws IOException, ClassNotFoundException
    {
        DescriptorRepository result = null;

        LoggerFactory.getBootLogger().info(
            "loading serialized repository " + serFile.getAbsolutePath());

        long start = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream(serFile);
        BufferedInputStream bis = new BufferedInputStream(fis);

        ObjectInputStream ois = new ObjectInputStream(bis);

        result = (DescriptorRepository) ois.readObject();
        long stop = System.currentTimeMillis();
        LoggerFactory.getBootLogger().info(
            "loading serialized took " + (stop - start) + " msecs");
        return result;
    }

    /**
     *
     * Builds the repository from a .jdo file.
     *
     * @author Travis Reeder - travis@spaceprogram.com
     */
    private DescriptorRepository buildRepositoryFromJdo(String repositoryFileName)
        throws
            MalformedURLException,
            ParserConfigurationException,
            SAXException,
            IOException
    {
        String xmlfile = null;
        Logger logger = LoggerFactory.getDefaultLogger();
        //j2ee compliant lookup of resources
        URL url =
            Thread.currentThread().getContextClassLoader().getResource(
                repositoryFileName);

        // don't be too strict: if resource is not on the classpath, try ordinary file lookup
        if (url == null)
        {
            File repFile = new File(repositoryFileName);
            if (!repFile.exists())
            {
                //
                // Try using the location of the OJB.properties file
                String strPath = System.getProperty("OJB.properties");
                if (strPath != null && strPath.length() > 0)
                {
                    File ojbProp = new File(strPath);
                    // make sure the file exists
                    if (ojbProp.exists())
                    {
                        int nInd1 = strPath.lastIndexOf('/');
                        int nInd2 = strPath.lastIndexOf('\\');
                        int nInd = Math.max(nInd1, nInd2);
                        if (nInd > 0)
                        {
                            //
                            // use the path name of the OJB.properties file to 
                            // determine the location of the repository.xml file
                            repositoryFileName =
                                strPath.substring(0, nInd + 1)
                                    + repositoryFileName;
                            repFile = new File(repositoryFileName);
                        }
                    }
                }
            }
            try
            {
                url = repFile.toURL();
            }
            catch (MalformedURLException ignore)
            {
            }

        }

        if (url != null)
        {
            xmlfile = url.toString();
            logger.info("OJB Descriptor Repository: " + xmlfile);
        }
        else
        {
            throw new MalformedURLException(
                "did not find resource " + repositoryFileName);
        }

        long start = System.currentTimeMillis();

        // get a xml reader instance:
        XMLReader reader = null;
        try
        {
            reader = XMLReaderFactory.createXMLReader();
        }
        catch (SAXException ex)
        {
            reader =
                XMLReaderFactory.createXMLReader(
                    "org.apache.crimson.parser.XMLReaderImpl");
        }

        // create an empty repository:
        DescriptorRepository repository = new DescriptorRepository();
        // create handler for building the repository structure
        ContentHandler handler = new OjbJdoXmlHandler(repository);
        // tell parser to use our handler:
        reader.setContentHandler(handler);
        reader.parse(xmlfile);
        long stop = System.currentTimeMillis();
        LoggerFactory.getBootLogger().info(
            "loading XML took " + (stop - start) + " msecs");

        logger.info("...Finished parsing");

        return repository;
    }

    private DescriptorRepository buildRepository(String repositoryFileName)
        throws
            MalformedURLException,
            ParserConfigurationException,
            SAXException,
            IOException
    {
        String xmlfile = null;
        Logger logger = LoggerFactory.getDefaultLogger();
        //j2ee compliant lookup of resources  
        URL url =
            Thread.currentThread().getContextClassLoader().getResource(
                repositoryFileName);

        // don't be too strict: if resource is not on the classpath, try ordinary file lookup 
        if (url == null)
        {
            File repFile = new File(repositoryFileName);
            if (!repFile.exists())
            {
                //
                // Try using the location of the OJB.properties file
                String strPath = System.getProperty("OJB.properties");
                if (strPath != null && strPath.length() > 0)
                {
                    File ojbProp = new File(strPath);
                    // make sure the file exists
                    if (ojbProp.exists())
                    {
                        int nInd1 = strPath.lastIndexOf('/');
                        int nInd2 = strPath.lastIndexOf('\\');
                        int nInd = Math.max(nInd1, nInd2);
                        if (nInd > 0)
                        {
                            //
                            // use the path name of the OJB.properties file to 
                            // determine the location of the repository.xml file
                            repositoryFileName =
                                strPath.substring(0, nInd + 1)
                                    + repositoryFileName;
                            repFile = new File(repositoryFileName);
                        }
                    }
                }
            }
            try
            {
                url = repFile.toURL();
            }
            catch (MalformedURLException ignore)
            {
            }
        }

        if (url != null)
        {
            xmlfile = url.toString();
            logger.info("OJB Descriptor Repository: " + xmlfile);
        }
        else
        {
            throw new MalformedURLException(
                "did not find resource " + repositoryFileName);
        }

        long start = System.currentTimeMillis();

        // get a xml reader instance: 
        SAXParser p = SAXParserFactory.newInstance().newSAXParser();
        XMLReader reader = null;
        reader = p.getXMLReader();
        reader.setFeature("http://xml.org/sax/features/validation", true);

        // 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(xmlfile);
        long stop = System.currentTimeMillis();
        //LoggerFactory.getBootLogger().info("loading XML took " + (stop - start) + " msecs");

        logger.info("...Finished parsing");

        return repository;
    }

    public void serialize(DescriptorRepository repository, File file)
    {
        try
        {
            FileOutputStream fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(repository);

            oos.flush();
            oos.close();
            fos.flush();
            fos.close();
        }
        catch (Throwable t)
        {
            LoggerFactory.getDefaultLogger().error(
                "Could not write to file" + file.getAbsolutePath(),
                t);
        }
    }

}
