/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
*/
package fr.gouv.culture.sdx.repository;

import fr.gouv.culture.sdx.document.Document;
import fr.gouv.culture.sdx.document.ParsableDocument;
//import fr.gouv.culture.sdx.documentbase.DocumentBase;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
//import fr.gouv.culture.sdx.oai.DocumentBaseOAIHarvester;
import fr.gouv.culture.sdx.utils.Utilities;
import fr.gouv.culture.sdx.utils.constants.ContextKeys;
import fr.gouv.culture.sdx.utils.constants.Node;
//import fr.gouv.culture.sdx.utils.database.Database;
//import fr.gouv.culture.sdx.utils.database.DatabaseConnection;
import fr.gouv.culture.sdx.utils.save.SaveParameters;
import fr.gouv.culture.sdx.utils.save.Saveable;

/*MAJ_COCOON_2.1.10
import org.apache.avalon.excalibur.io.IOUtil;*/
import org.apache.commons.io.IOUtils;
import org.apache.excalibur.xml.sax.SAXParser;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.cocoon.xml.XMLConsumer;
import org.xml.sax.ContentHandler;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
//import java.io.FileInputStream;
import java.io.FileOutputStream;
//import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.*;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/** An implementation of a repository using a JDBC datasource.
 * <p>
 * This repository can be used by itself, or be subclassed for specific RDBMS.
 * A great care has been taken to isolate what could be vendor specific.
 */
public class JDBCRepository extends AbstractDataSourceComponentBackedRepository {

    public static final int NO_SPECIFIC_TYPE = -1;


    /**Constant for a field name in a table*/
    protected final static String FIELD_DATA = "data";
    /**Max number of entries to load during the saving processus*/
    private final static long RESULT_MAX_SIZE = 500;


    protected final int PARAM_INDEX_FIELD_DATA_ORIGINAL = 2;


    /**
     * Creates a repository.
     *
     * <p>
     * A super.getLog() must be set and then this repository must be configured and initialized.
     *
     * @see #enableLogging
     * @see #configure
     * @see #init
     */
    public JDBCRepository() {
    }


    /** Gets a connection for manipulating repository's content.
     * @return The connection.
     * @throws SDXException
     */
    public RepositoryConnection getConnection() throws SDXException {
        try {
            JDBCRepositoryConnection conn = new JDBCRepositoryConnection(getDataSourceComponent());
            conn.enableLogging(super.getLog());
            return conn;
        } catch (SQLException e) {
            String[] args = new String[2];
            args[0] = getId();
            args[1] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_CONNECTION, args, e);
        } catch (ServiceException e) {
            String[] args = new String[1];
            args[0] = e.getMessage();
            //null super.getLog() passed to prevent double logging
            SDXException sdxE = new SDXException(null, SDXExceptionCode.ERROR_ACQUIRE_DATASOURCE, args, e);

            String[] args2 = new String[2];
            args2[0] = getId();
            args2[1] = sdxE.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_CONNECTION, args2, sdxE);
        }
    }

    /** Releases a previously opened connection.
     *
     * The releases both the SQL connection to the database
     * and the datasource service connection from cocoon's
     * database selector service.
     *
     * @param c A connection.
     * @throws SDXException
     */
    public void releaseConnection(RepositoryConnection c) throws SDXException {
        if (c != null) {
            //closing the sql connection if there is one open
            if (((JDBCRepositoryConnection) c).getConnection() != null)
                ((JDBCRepositoryConnection) c).releaseSqlConnection();
            //releasing the datasource service from cocoon's dbSelector
            this.releaseDataSourceComponent(((JDBCRepositoryConnection) c).getDataSource());
        }
    }

    /** Configures this repository.
     *
     * <p>
     *  In addition to the parameters needed in the base configuration handled by the parent class,
     *  the following parameter is required: data source _identifier (dsi), the value of the "name"
     *  attribute of the "jdbc" subElement for the  "datasources" element in cocoon.xconf
     *  (user must create this information in cocoon.xconf)
     *
     * @param   configuration   The configuration for this repository (based on a xml file).
     *
     *<p> Sample configuration entry:
     *<p> &lt;sdx:repository sdx:type = "JDBC" sdx:id = "myRepoId" dsi = "datasource _identifier from cocoon.xconf"/>
     *
     */
    public void configure(Configuration configuration) throws ConfigurationException {
        // Let the superclass handle basic configurations
        loadBaseConfiguration(configuration);

        // building a name for the table, it is the appPathName(app directory name) and the repositoryId separated by an underscore, appPath_repoId,
        /*TODO: maybe in the future if the need arises, but this method of building the table name isn't portable
        *if the user decides to change the app name when moving the app, as the table name would change too
        */
        this.tableName = Utilities.getStringFromContext(ContextKeys.SDX.Application.DIRECTORY_NAME, getContext()) + "_" + super.getId();
    }

    /** Initializes the repository.
     *
     * If there are no tables in the database,
     * we create the necessary table
     *
     * @throws SDXException  */
    public void init() throws SDXException {

        /** First try to access the database and see if the tables exist. */
        Connection conn = null;
        ResultSet rs = null;

        JDBCRepositoryConnection repoConn = (JDBCRepositoryConnection) getConnection();
        try {
            conn = repoConn.getConnection();
            DatabaseMetaData dbmd = conn.getMetaData();
            String tableName = getTableName();
            StringTokenizer tnTokens = new StringTokenizer(tableName, "_", true);
            String escapedTableName = "";
            while (tnTokens.hasMoreTokens()) {
                String nextToken = tnTokens.nextToken();
                if (nextToken.equals("_") || nextToken.equals("%"))
                    nextToken = dbmd.getSearchStringEscape() + nextToken;
                escapedTableName += nextToken;
            }
            if (Utilities.checkString(escapedTableName))
                tableName = escapedTableName;
            rs = dbmd.getTables(null, null, tableName, null);
            if (!rs.next()) {
                //we don't have a table name with mixed case
                //we will try with lower case
                rs = dbmd.getTables(null, null, tableName.toLowerCase(), null);                // The table doesn't exist, so we should create it.
                if (!rs.next()) {
                    //we don't have a table name with lower case
                    //we will try with upper case
                    rs = dbmd.getTables(null, null, tableName.toUpperCase(), null);                // The table doesn't exist, so we should create it.
                    if (!rs.next())//no table we will create it
                        createTable(conn);
                }
            }
        } catch (SQLException e) {
            String[] args = new String[2];
            args[0] = getId();
            args[1] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_INIT_REPO, args, e);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    String[] args = new String[2];
                    args[0] = getId();
                    args[1] = e.getMessage();
                    throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CLOSE_RESULT_SET, args, e);
                }
            }
            repoConn.commit();
            //this closes the connection
            releaseConnection(repoConn);

        }
    }

    /** Returns the number of documents within the repository (all kind of documents).
     *
     * NOT IMPLEMENTED YET!
     *
     * @return The number.
     * @throws SDXException
     */
    public long size() throws SDXException {
        return size(NO_SPECIFIC_TYPE);
    }

    /** Returns the number of documents of specified type within the store.
     *
     *	@param	type	The type of document, must be defined in fr.gouv.culture.sdx.documents.Document constants.
     */
    private long size(final int type) throws SDXException {

        JDBCRepositoryConnection repoConn = (JDBCRepositoryConnection) getConnection();
        Connection conn = null;
        try {
            conn = repoConn.getConnection();
            String queryString = "SELECT count(*) FROM ?";
            if (type != NO_SPECIFIC_TYPE)
                queryString += " WHERE " + FIELD_DATA + "=?";//at this point we have "SELECT count(*) FROM ?  WHERE data=type"
            Template template = new Template(conn, queryString);
            QueryExecutor qe = new QueryExecutor() {
                int l_value = 0;

                public void prepare(PreparedStatement ps) throws SQLException {
                    ps.setString(1, getTableName());
                    ps.setInt(2, type);
                }

                public void collect(ResultSet rs) throws SQLException {
                    if (rs.next()) l_value = rs.getInt(1);
                }

                public int value() {
                    return l_value;
                }
            };
            template.execute(qe, Template.MODE_EXECUTE_QUERY);
            return qe.value();
        } catch (SDXException e) {
            String[] args = new String[2];
            args[0] = getId();
            args[1] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_SIZE_REPO, args, e);
        } finally {
            //this closes the connection
            releaseConnection(repoConn);
        }

    }

    /** Lists the repository content as SAX events.
     * <p>
     * The exact structure is still to be defined, but it should be very simple,
     * with only one element per document and a few properties as attributes.
     *
     * @param hdl A SAX content handler to feed with events.
     * @throws SDXException
     */
    public void lists(ContentHandler hdl) throws SDXException {
        //TODOImplement
    }

    /** Adds a document to the repository.
     *
     * @param doc   A document.
     * @param c     A connection to the repository.
     * @throws SDXException
     */
    public synchronized void add(Document doc, RepositoryConnection c) throws SDXException {
        //ensuring we have valid objects
        super.add(doc, c);
        Connection conn = ((JDBCRepositoryConnection) c).getConnection();
        String queryString = getDocumentAddQuery();
        try {
            Template template = new Template(conn, queryString);
            final String docId = doc.getId();
            final InputStream is = doc.openStream();
            final int length = is.available();
            QueryExecutor qe = new QueryExecutor() {
                public void prepare(PreparedStatement ps) throws SQLException {
                    ps.setString(PARAM_INDEX_FIELD_ID, docId);
                    //this is will give us the original file
                    ps.setBinaryStream(PARAM_INDEX_FIELD_DATA_ORIGINAL, is, length);
                }

            };
            template.execute(qe, Template.MODE_EXECUTE_UPDATE);
        } catch (SDXException e) {
            String[] args = new String[3];
            args[0] = doc.getId();
            args[1] = getId();
            args[2] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_ADD_DOC, args, e);
        } catch (IOException e) {
            String[] args = new String[3];
            args[0] = doc.getId();
            args[1] = getId();
            args[2] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_ADD_DOC, args, e);
        }

    }

    /** Deletes all documents from the repository.
     * @throws SDXException
     */
    public synchronized void empty() throws SDXException {
        Connection conn = null;
        JDBCRepositoryConnection repoConn = (JDBCRepositoryConnection) getConnection();

        try {
            conn = repoConn.getConnection();
            String queryString = getDeleteAllQuery();
            Template template = new Template(conn, queryString);
            QueryExecutor qe = new QueryExecutor() {
            };
            template.execute(qe, Template.MODE_EXECUTE_UPDATE);
        } catch (SDXException e) {
            String[] args = new String[2];
            args[0] = getId();
            args[1] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_EMPTY, args, e);
        } finally {
            repoConn.commit();
            //this closes the connection
            releaseConnection(repoConn);
        }
    }

    /** Deletes a document from the repository.
     *
     * @param doc A document.
     * @param c A connection to the repository.
     * @throws SDXException
     */
    public synchronized void delete(Document doc, RepositoryConnection c) throws SDXException {
        super.delete(doc, c);
        Connection conn = ((JDBCRepositoryConnection) c).getConnection();
        String queryString = getDocumentDeleteQuery();
        try {
            Template template = new Template(conn, queryString);
            final String docId = doc.getId();
            QueryExecutor qe = new QueryExecutor() {
                public void prepare(PreparedStatement ps) throws SQLException {
                    ps.setString(PARAM_INDEX_FIELD_ID, docId);
                }
            };
            template.execute(qe, Template.MODE_EXECUTE_UPDATE);
        } catch (SDXException e) {
            String[] args = new String[3];
            args[0] = doc.getId();
            args[1] = getId();
            args[2] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_DELETE_DOC, args, e);
        }

    }

    /** Retrieves an SDX document as SAX events.
     *
     * @param doc       A ParsableDocument, ie XMLDocument or HTMLDocument.
     * @param consumer  A SAX content handler to feed with events.
     * <p>The wrapped contentHandler for including events within an XSP page contentHandler should be created using
     * IncludeXMLConsumer stripper = new IncludeXMLConsumer(xspContentHandler);</p>
     * @param c A connection to the repository.
     * @throws SDXException
     */
    public void toSAX(ParsableDocument doc, XMLConsumer consumer, RepositoryConnection c) throws SDXException {
        /*TODORefactor?:can we refactor these methods into abstract repository given the changes i made there with the
        *necessity to make specific calls to the openStream methods of the subclasses?-rbp
        */
        //ensuring we have valid objects
        super.toSAX(doc, consumer, c);
        SAXParser parser = null;
        ServiceManager l_manager = super.getServiceManager();
        try {
            doc.setContent(openStream(doc, null, c));
            parser = (SAXParser) l_manager.lookup(SAXParser.ROLE);
            doc.parse(parser, consumer);
        } catch (ServiceException e) {
            String[] args = new String[1];
            args[0] = e.getMessage();
            //null super.getLog() passed to prevent double logging
            SDXException sdxE = new SDXException(null, SDXExceptionCode.ERROR_ACQUIRE_PARSER, args, e);

            String[] args2 = new String[2];
            args2[0] = getId();
            args2[1] = sdxE.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_DOC, args2, sdxE);
        } /*catch (SDXException e) {
            //is this catch necessary, i think the finally will be exectued either way, but for safety we'll catch it now?-rbp
            throw e;
        } */ finally {
            if (parser != null) l_manager.release(parser);
        }
    }

    /** Opens an input stream to read the content of a document.
     *
     * @return          The stream.
     * @param encoding  An encoding (may be null).
     * <p>If <code> null</code> or invalid we use a default.
     * <p>TODOImplement use of encoding currently not implemented , will do soon.
     * @param doc       A document to read.
     * @param c         A connection to the repository.
     * @throws SDXException
     */
    public InputStream openStream(Document doc, String encoding, RepositoryConnection c) throws SDXException {
        //ensuring we have valid objects
        super.openStream(doc, encoding, c);
        Connection conn = ((JDBCRepositoryConnection) c).getConnection();
        String queryString = getDocumentGetQuery();
        try {
            Template template = new Template(conn, queryString);
            final String docId = doc.getId();
            QueryExecutor qe = new QueryExecutor() {
                InputStream l_is = null;

                public void prepare(PreparedStatement ps) throws SQLException {
                    ps.setString(PARAM_INDEX_FIELD_ID, docId);
                }

                public void collect(ResultSet rs) throws SQLException {
                    if (rs.next())
                    //was FIELD_DATA_TRANSFORMED, but since we dont store that yet, it is no good
                        l_is = rs.getBinaryStream(FIELD_DATA);
                }

                public InputStream getInputStream() {
                    return l_is;
                }
            };
            template.execute(qe, Template.MODE_EXECUTE_QUERY);
            InputStream is = qe.getInputStream();
            if (is == null) {
                String[] args = new String[2];
                args[0] = doc.getId();
                args[1] = getId();
                throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_DOC, args, null);
            }
            return is;
        } catch (SDXException e) {
            String[] args = new String[3];
            args[0] = doc.getId();
            args[1] = getId();
            args[2] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_DOC, args, e);
        }

    }

    /** Writes the content of a document to an output stream.
     *
     * @param doc A document.
     * @param os An output stream.
     * @param c A connection to the repository.
     * @throws SDXException
     */
    public void get(Document doc, final OutputStream os, RepositoryConnection c) throws SDXException {
        //ensuring we have valid objects
        super.get(doc, os, c);
        Connection conn = ((JDBCRepositoryConnection) c).getConnection();
        String queryString = getDocumentGetQuery();
        try {
            Template template = new Template(conn, queryString);
            final String docId = doc.getId();
            QueryExecutor qe = new QueryExecutor() {
                public void prepare(PreparedStatement ps) throws SQLException {
                    ps.setString(PARAM_INDEX_FIELD_ID, docId);
                }

                public void collect(ResultSet rs) throws SQLException, IOException {
                    if (rs.next()) IOUtils.copy(rs.getBinaryStream(FIELD_DATA), os);
                }
            };
            template.execute(qe, Template.MODE_EXECUTE_QUERY);
        } catch (SDXException e) {
            String[] args = new String[3];
            args[0] = doc.getId();
            args[1] = getId();
            args[2] = e.getMessage();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_DOC, args, e);
        }

    }

    /** Returns an SQL query that could create the table.
     * <p>
     * This query should not have any parameter.
     * @return The query.
     */
    protected String getTableCreationQuery() {
        //changed dataType of FIELD_DATA_TRANSFORMED from "BINARY NOT NULL" TO only "BINARY"
        return "CREATE TABLE " + getTableName() + " ( " + FIELD_ID + " VARCHAR(255) NOT NULL, " + FIELD_DATA + " BINARY, PRIMARY KEY (" + FIELD_ID + "))";
    }

    /** Returns an SQL query that could add a document.
     * <p>
     * This query should have three parameters, the first for the id, the second
     * for the doctype and the third for the data.
     * @return The query.
     */
    protected String getDocumentAddQuery() {
        //TODOImplement: at some time, implement the proper handling of the original document (column data_t).
        return "INSERT INTO " + getTableName() + " (" + FIELD_ID + "," + FIELD_DATA + ") VALUES (?, ?)";
    }

    /** Returns an SQL query that could delete a document.
     * <p>
     * This query should have one parameter for the document's id.
     * @return The query.
     */
    protected String getDocumentDeleteQuery() {
        return "DELETE FROM " + getTableName() + " WHERE " + FIELD_ID + " = ?";
    }

    /** Returns an SQL query that could delete all documents.
     * <p>
     * This query should not have any parameter.
     * @return The query.
     */
    protected String getDeleteAllQuery() {
        return "DELETE FROM " + getTableName();
    }

    /**
     * Implementation of statement with a limit parameter
     * No limit there, implementation is done in the subclasses.
     * @param offset
     * @param number
     * @return the query
     */
    protected String getEntriesWithLimitQuery(long offset, long number)
    {
    	String query = "SELECT * FROM " + getTableName() + ";";
		return query;
    }


	/* (non-Javadoc)
	 * @see fr.gouv.culture.sdx.utils.AbstractSdxObject#initToSax()
	 */
	protected boolean initToSax() {
		this._xmlizable_objects.put("ID", this.getId());
    	this._xmlizable_objects.put("Encoding", this.getEncoding());
    	this._xmlizable_objects.put("XML-Lang", this.getXmlLang());
    	this._xmlizable_objects.put("Is_default",String.valueOf(this.isDefault()));
    	try{
    		this._xmlizable_objects.put("Document_Count",String.valueOf(this.size()));
    	}catch(SDXException e){
    	}
    	this._xmlizable_objects.put("Repository_Type",this.getClass().getName());
    	if(dsi != null)
    		this._xmlizable_objects.put("Data_Source_Identifier",this.dsi);
    	else
    		this._xmlizable_objects.put("Data_Source_Identifier","none");

		return true;
	}

	/**Init the LinkedHashMap _xmlizable_volatile_objects with the objects in order to describ them in XML
	 * Some objects need to be refresh each time a toSAX is called*/
	protected void initVolatileObjectsToSax() {
		this._xmlizable_objects.put("Is_default",String.valueOf(this.isDefault()));
    	try{
    		this._xmlizable_objects.put("Document_Count",String.valueOf(this.size()));
    	}catch(SDXException e){
    	}
	}

	/** Save the repository
	 * @see fr.gouv.culture.sdx.utils.save.Saveable#backup(fr.gouv.culture.sdx.utils.save.SaveParameters)
	 */
	public void backup(SaveParameters save_config) throws SDXException {
		if(save_config != null)
		{
			String repo_path = save_config.getUniqueIDString() + "_repo";
			if(save_config.getAttributeAsBoolean(Saveable.ALL_SAVE_ATTRIB,false))
			{
				// add the path to the save_config
				save_config.savePathInConfig(repo_path);

				// Create the documentBases/id_db/repositories/id_repo directory
				File repo_dir = new File(save_config.getStoreCompletePath() + File.separator + repo_path);
				if(!repo_dir.exists())
					repo_dir.mkdir();
				save_config.setAttribute(Node.Name.ID,this.getId());

				try{
					FileOutputStream fo = new FileOutputStream(save_config.getStoreCompletePath() + File.separator +
																repo_path+File.separator+"repo.zip");
					backupRepositoryData(fo);
				}catch(IOException e)
				{
					String[] args = new String[2];
		            args[0] = this.getId();
		            args[1] = e.getMessage();
		            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, e);
				}


			}
		}
	}

	/**
	 * Save the documents and their ids in a Zip file
	 * @param fo the file where data will be stored (a zip file)
	 */
	private void backupRepositoryData(FileOutputStream fo) throws SDXException
	{
		//Create the ZipOutputStream
		ZipOutputStream zos = new ZipOutputStream(fo);
		ZipOutputStream zos_temp = null;
		int count = 0;
		//Get a connection with repository database
		RepositoryConnection sqlrepoConn = getConnection();
        Connection conn = ((JDBCRepositoryConnection)sqlrepoConn).getConnection();
		try{

			Statement st = conn.createStatement();

			//Get the number of entries
			ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM " + getTableName());
			long num_entries=0;
			while(rs.next())
				num_entries = rs.getLong(1);

			//Calculate how many request to make with a max batch of RESULT_MAX_SIZE
			long max_batch = num_entries / RESULT_MAX_SIZE;
			if( (num_entries % RESULT_MAX_SIZE) != 0)
				max_batch++;

			for(long i=0; i<max_batch; i++)
			{

				if((i % 100) == 0)
				{
					if(i != 0)
						zos_temp.finish();
					zos.putNextEntry(new ZipEntry(String.valueOf(i/100)+".zip"));
					zos_temp = new ZipOutputStream(zos);
					count = 0;
				}
				rs = st.executeQuery(this.getEntriesWithLimitQuery(i*RESULT_MAX_SIZE, RESULT_MAX_SIZE));
				while(rs.next())
				{
					zos_temp.putNextEntry(new ZipEntry(String.valueOf(count++)+".dat"));
					zos_temp.write((rs.getString(FIELD_ID)+"\n").getBytes());
					zos_temp.write(rs.getBytes(FIELD_DATA));
				}
			}
			zos_temp.finish();

		}catch(SQLException e)
		{
			String[] args = new String[2];
            args[0] = this.getId();
            args[1] = e.getMessage();
            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, e);
		}
		catch(IOException e)
		{
			String[] args = new String[2];
            args[0] = this.getId();
            args[1] = e.getMessage();
            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, e);
		}
		finally
		{
			this.releaseConnection(sqlrepoConn);
			try{
				zos.close();
			}catch(IOException ee)
			{
				String[] args = new String[2];
	            args[0] = this.getId();
	            args[1] = ee.getMessage();
	            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, ee);
			}
		}
	}

	/** Restore the repository
	 * @see fr.gouv.culture.sdx.utils.save.Saveable#restore(fr.gouv.culture.sdx.utils.save.SaveParameters)
	 */
	public void restore(SaveParameters save_config) throws SDXException {
		if(save_config != null)
		{
			//Get the absolute directory where the repository file is
			String repo_path = save_config.getStoreCompletePath();
			if(save_config.getAttributeAsBoolean(Saveable.ALL_SAVE_ATTRIB,false))
			{
				//chack for existence
				File file = new File(repo_path + File.separator + "repo.zip");
				if(file.exists())
					restoreRepositoryData(file); //restore data
			}
		}
	}

	/**
	 * Restore the documents and their ids from a Zip file
	 * @param fo the file where data is stored (a zip file)
	 */
	private void restoreRepositoryData(File file) throws SDXException
	{
//		Get a connection with repository database
		RepositoryConnection sqlrepoConn = getConnection();
        Connection conn = ((JDBCRepositoryConnection)sqlrepoConn).getConnection();
		ZipFile zf = null;

		try{
			//Create the ZipInputStream
			zf = new ZipFile(file);
			ZipInputStream zis_temp = null;

	        //Create a transaction
			conn.setAutoCommit(false);
			Statement st = conn.createStatement();
			int len = 0;
			byte[] b_temp = new byte[1024];
			ByteArrayOutputStream bo_id = new ByteArrayOutputStream();
			ByteArrayOutputStream bo_blob = new ByteArrayOutputStream(512);

			//Delete the previous table content
			st.executeUpdate("DELETE FROM " + getTableName());
			//Add all saved documents
			for(Enumeration _enum = zf.entries(); _enum.hasMoreElements();)
			{
				//Get a document batch : 50000 documents
				ZipEntry ze = (ZipEntry)_enum.nextElement();
				zis_temp = new ZipInputStream(zf.getInputStream(ze));
				while(zis_temp.getNextEntry()!=null)
				{
					//get all documents in the document batch
					boolean end_id = false;
					bo_id.reset();
					bo_blob.reset();
					//parse the entry to get the id and the document
					while((len = zis_temp.read(b_temp))>0)
					{
						if(!end_id) //Is the id found and complete?
						{
							//get the id
							int i=0;
							while(i<len && !end_id)
							{
								if(b_temp[i]=='\n')
								{
									end_id = true;
									bo_id.write(b_temp,0,i);
									bo_blob.write(b_temp,i+1,len-i-1);
								}
								i++;
							}
						}
						else
						{
							//get the blob
							bo_blob.write(b_temp,0,len);
						}
					}

					//Get the query to add a document in the database
					String queryString = getDocumentAddQuery();

					//Format and prepare the query
		            Template template = new Template(conn, queryString);
		            final String docId = bo_id.toString();
		            final InputStream is = new ByteArrayInputStream(bo_blob.toByteArray());
		            final int length = is.available();
		            QueryExecutor qe = new QueryExecutor() {
			                public void prepare(PreparedStatement ps) throws SQLException {
			                    ps.setString(PARAM_INDEX_FIELD_ID, docId);
			                    //this is will give us the original file
			                    ps.setBinaryStream(PARAM_INDEX_FIELD_DATA_ORIGINAL, is, length);
			                }

			            };

			        //send the query to the database
			        template.execute(qe, Template.MODE_EXECUTE_UPDATE);
				}
		        zis_temp.close();
			}
			conn.commit();

		}catch(SQLException e)
		{
			try{
				conn.rollback();
			}catch(SQLException ee){
				String[] args = new String[2];
	            args[0] = this.getId();
	            args[1] = ee.getMessage();
	            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, ee);
			}
			String[] args = new String[2];
            args[0] = this.getId();
            args[1] = e.getMessage();
            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, e);
		}
		catch(IOException e)
		{
			try{
				conn.rollback();
			}catch(SQLException ee){
				String[] args = new String[2];
	            args[0] = this.getId();
	            args[1] = ee.getMessage();
	            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, ee);
			}
			String[] args = new String[2];
            args[0] = this.getId();
            args[1] = e.getMessage();
            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, e);
		}
		finally
		{
			try{
				if(conn !=null)
					conn.setAutoCommit(true);
				zf.close();
			}catch(SQLException ee){
				String[] args = new String[2];
	            args[0] = this.getId();
	            args[1] = ee.getMessage();
	            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, ee);
			}catch(IOException ee){
				String[] args = new String[2];
	            args[0] = this.getId();
	            args[1] = ee.getMessage();
	            throw new SDXException(getLog(), SDXExceptionCode.ERROR_BACKUP_REPOSITORY, args, ee);
			}
			this.releaseConnection(sqlrepoConn);
		}
	}
}
