/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2011 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * This library 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 any later version.
 *
 * 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
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.db.h2;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.h2.engine.Constants;
import org.h2.tools.Server;
import org.ow2.jonas.db.DBService;
import org.ow2.jonas.db.base.AbsDBServiceImpl;
import org.ow2.jonas.db.base.AbsDBServiceImplMBean;
import org.ow2.jonas.db.base.User;
import org.ow2.jonas.service.ServiceException;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

/**
 * Embeds the H2 database in JOnAS.
 * @author Florent Benoit
 */
public class H2DBServiceImpl extends AbsDBServiceImpl implements DBService, H2DBServiceImplMBean {

    /**
     * Logger.
     */
    private Log logger = LogFactory.getLog(H2DBServiceImpl.class);

    /**
     * List of users.
     */
    private List<User> users = null;

    /**
     * Name of database.
     */
    private String databaseName = null;

    /**
     * Default port number.
     */
    private static final String DEFAULT_PORT = "9001";

    /**
     * H2 server.
     */
    private Server server = null;

    /**
     * Extra args list.
     */
    private List<String> extraArgs = null;

    /**
     * Create a database with the specified arguments.
     * @param users user/password (separated by a ":")
     * @param databaseName name of the database
     * @param portNumber port number of the database
     */

    @Override
    protected void initServer(final List<User> users, final String databaseName, final String portNumber) {
        this.users = users;
        if (portNumber != null) {
            this.portNumber = portNumber;
        } else {
            this.portNumber = DEFAULT_PORT;
        }
        this.databaseName = databaseName;

        String baseDir = getServerProperties().getWorkDirectory() + File.separator + "h2" + File.separator + databaseName;

        // Build list of arguments
        List<String> argList = new ArrayList<String>();

        // Specify TCP port
        argList.add("-tcpPort");
        argList.add(this.portNumber);

        // Specify base directory
        argList.add("-baseDir");
        argList.add(baseDir);

        // extra args
        if (extraArgs != null && extraArgs.size() > 0) {
            argList.addAll(extraArgs);
        }

        // Convert args into array
        String[] args = argList.toArray(new String[argList.size()]);

        // Create server instance
        try {
            server = Server.createTcpServer(args);
        } catch (SQLException e) {
            throw new ServiceException("Unable to init the server", e);
        }

    }

    /**
     * Start the service.
     * @throws ServiceException if the startup failed.
     */
    @Override
    protected void doStart() throws ServiceException {
        super.doStart();
        logger.info("Starting H2 Server ''{0}'' on port ''{1}''", Constants.getVersion(), portNumber);

        try {
            server.start();
        } catch (SQLException e) {
            throw new ServiceException("Unable to start the H2 server", e);
        }

        Connection conn = null;
        Statement st = null;
        try {
            Class.forName("org.h2.Driver");
            conn = DriverManager.getConnection("jdbc:h2:tcp://localhost:" + portNumber + "/" + databaseName, "sa",
                    "");
            st = conn.createStatement();
        } catch (Exception e) {
            throw new ServiceException("Cannot access to H2", e);
        }

        // Drop users before recreating it
        User user = null;
        String userName = null;
        String password = null;
        for (Iterator<User> it = users.iterator(); it.hasNext();) {
            user = it.next();
            try {
                password = user.getPassword();
                userName = user.getUserName();
                logger.info("Dropping and adding user ''{0}'' with password ''{1}''.", userName, password);
                try {
                    st.execute("DROP USER " + userName);
                } catch (Exception e) {
                    logger.debug("User ''{0}'' doesn't exists", userName, e);
                }
                st.execute("Create USER " + userName + " PASSWORD '" + password + "' ADMIN");
            } catch (Exception e) {
                logger.error("Error while creating/adding user", e);
            }
        }

        try {
            st.close();
        } catch (Exception e) {
            logger.debug("Error while closing statement object", e);
        }
    }

    /**
     * Define extra arguments that can be set on the server initialization.
     * @param extraArgs the list of arguments to use
     */
    public void setExtraArgs(final String extraArgs) {
        this.extraArgs = convertToList(extraArgs);
    }

    /**
     * Stop the service.
     * @throws ServiceException if the stop failed.
     */
    @Override
    protected void doStop() throws ServiceException {
        super.doStop();

        server.stop();
        logger.info("H2 Server ''{0}'' stopped.", Constants.getVersion());

    }
}
