/**
 * JaDOrT: JASMINe Deployment Orchestration Tool
 * Copyright (C) 2009 Bull S.A.S.
 * Contact: jasmine@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: OSGiServerAction.java 3883 2009-06-08 14:30:06Z alitokmen $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.service.action;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;

import org.ow2.jasmine.jadort.api.entities.deployment.ApplicationBean;
import org.ow2.jasmine.jadort.api.entities.topology.ConnectorBean;
import org.ow2.jasmine.jadort.api.entities.topology.ServerBean;

/**
 * Action for an OSGi server
 * 
 * @author Arda Aydin
 * @author Malek Chahine
 * @author S. Ali Tokmen
 */
public class OSGiServerAction extends ServerAction {

    private String name;

    private ConnectorBean serverConnector;

    private ObjectName osgiServerObjectName;

    public OSGiServerAction(final ServerBean server) {
        this.name = server.getName();
        this.serverConnector = server.getServerConnector();
        this.appendToLog("Created OSGiServerAction for server '" + this.name + "'");
    }

    @Override
    public boolean canDeployApplications() throws Exception {
        return true;
    }

    @Override
    public boolean canStartStopServer() throws Exception {
        return false;
    }

    @Override
    public void deploy(final String bundleId) throws Exception {
        this.checkJMXConnection();

        this.appendToLog("Starting bundle '" + bundleId + "'");

        Object opParamStart[] = {new Long(bundleId)};
        String opSigStart[] = {"long"};
        this.mbscnx.invoke(this.osgiServerObjectName, "startBundle", opParamStart, opSigStart);

        this.appendToLog("Call to BundleManager.startBundle successful for bundle '" + bundleId + "'");
    }

    @Override
    public void undeploy(final String bundleId) throws Exception {
        this.checkJMXConnection();

        this.appendToLog("Stopping bundle '" + bundleId + "'");

        Object opParamStart[] = {new Long(bundleId)};
        String opSigStart[] = {"long"};
        this.mbscnx.invoke(this.osgiServerObjectName, "stopBundle", opParamStart, opSigStart);

        this.appendToLog("Call to BundleManager.stopBundle successful for bundle '" + bundleId + "'");
    }

    @Override
    public boolean enableOrDisableApplications(final boolean enable) throws Exception {
        throw new UnsupportedOperationException("Operation not implemented yet !");
    }

    @Override
    public void erase(final String bundleId) throws Exception {
        this.checkJMXConnection();

        this.appendToLog("Uninstalling bundle '" + bundleId + "'");

        Object opParamStart[] = {new Long(bundleId)};
        String opSigStart[] = {"long"};

        this.mbscnx.invoke(this.osgiServerObjectName, "uninstallBundle", opParamStart, opSigStart);

        this.appendToLog("Call to BundleManager.uninstallBundle successful for bundle '" + bundleId + "'");

    }

    @Override
    public int getActiveSessions(final String appName) throws Exception {
        return 0;
    }

    @Override
    public int getActiveSessions() throws Exception {
        return 0;
    }

    @Override
    public ApplicationBean getApplicationBean(final String appName) throws Exception {
        this.checkJMXConnection();
        Long bundleId = new Long(appName);

        Object opParamStart[] = {bundleId};
        String opSigStart[] = {"long"};

        String bundleInfo = (String) this.mbscnx.invoke(this.osgiServerObjectName, "bundleInfo", opParamStart, opSigStart);

        if (bundleInfo != null) {
            return this.createApplicationBean(bundleId, bundleInfo);
        } else {
            return null;
        }
    }

    private ApplicationBean createApplicationBean(final Long id, final String bundleIdentifier) throws Exception {
        ApplicationBean aBean = new ApplicationBean();

        String symbolicName = bundleIdentifier.split(";")[0];
        String version = bundleIdentifier.split(";")[1];

        aBean.setName(symbolicName);
        aBean.setVersion(version);

        Object opParamStart[] = {new Long(id)};
        String opSigStart[] = {"long"};
        String state = (String) this.mbscnx.invoke(this.osgiServerObjectName, "stateOfBundle", opParamStart, opSigStart);
        aBean.setState(state);

        return aBean;
    }

    @Override
    public boolean isStarted() {
        try {
            this.checkJMXConnection();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<ApplicationBean> listOfApplications() throws Exception {
        this.checkJMXConnection();
        List<ApplicationBean> applications = new ArrayList<ApplicationBean>();

        Map<Long, String> bundlesMap = new Hashtable<Long, String>();
        bundlesMap = (Map<Long, String>) this.mbscnx.invoke(this.osgiServerObjectName, "allBundles", null, null);

        Iterator it = bundlesMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry) it.next();

            Long id = (Long) pairs.getKey();
            String bundleIdentifier = (String) pairs.getValue();

            applications.add(this.createApplicationBean(id, bundleIdentifier));
        }

        return applications;
    }

    @Override
    public String setDefault(final String appName) throws Exception {
        // There is no default bundle in OSGi.
        return appName;
    }

    @Override
    public void start() throws Exception {
        throw new UnsupportedOperationException("Operation not implemented yet !");
    }

    @Override
    public void stop() throws Exception {
        throw new UnsupportedOperationException("Operation not implemented yet !");
    }

    @Override
    public String upload(final ApplicationBean application) throws Exception {
        this.checkJMXConnection();

        this.appendToLog("Installing bundle for ApplicationBean '" + application + "'");

        String fileName = application.getName();
        Object param[] = {fileName, ServerAction.obtainByteData(application.getFile())};
        String sign[] = {String.class.getName(), byte[].class.getName()};
        long bundleId = (Long) this.mbscnx.invoke(this.osgiServerObjectName, "installBundle", param, sign);

        this.appendToLog("Call to BundleManager.installBundle successful for ApplicationBean '" + application
            + "', bundle identifier is '" + bundleId + "'");

        return String.valueOf(bundleId);
    }

    @Override
    @SuppressWarnings("unchecked")
    protected void connectViaJMX() throws Exception {
        String url = this.serverConnector.getConnectorUrl();
        String username = this.serverConnector.getUsername();
        String password = this.serverConnector.getPassword();

        Map<String, Object> env = new HashMap<String, Object>();
        if (username != null && password != null) {
            String[] creds = {username, password};
            env.put(JMXConnector.CREDENTIALS, creds);
        }
        this.establishJMXConnection(new JMXServiceURL(url), env);

        Set<ObjectName> bundleManagers = this.mbscnx.queryNames(new ObjectName(
            "*:type=jmx.bundle.manager.impl.BundleManagerImpl,*"), null);

        if (null == bundleManagers || bundleManagers.isEmpty()) {
            bundleManagers = this.mbscnx.queryNames(new ObjectName("*:type=BundleManager,instance=JmxBundleManager,*"), null);

            if (null == bundleManagers || bundleManagers.isEmpty()) {
                throw new Exception("JMXBundleManager not found on the OSGi framework");
            }
        }

        this.osgiServerObjectName = bundleManagers.iterator().next();

        this.appendToLog("JMX server connection OK for server '" + this.name + "', OSGiServer's bundleManager is '"
            + this.osgiServerObjectName + "'");
    }

}
