/**
 * JaDOrT: JASMINe Deployment Orchestration Tool
 * Copyright (C) 2008-2009 Bull S.A.S.
 * Copyright (C) 2008-2009 France Telecom R&D
 * 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: WebSphereServerAction.java 3808 2009-06-03 11:55:00Z alitokmen $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.service.action;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import javax.management.Attribute;
import javax.management.ObjectName;
import javax.management.remote.JMXServiceURL;
import javax.naming.Context;

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

/**
 * Action for the WebSphere server.
 * 
 * @author Malek Chahine
 * @author Remy Bresson
 * @author S. Ali Tokmen
 */
public class WebSphereServerAction extends ServerAction {

    private static final String WEBSPHERE_ROOT = "WAS_INSTALL_ROOT";

    private String name;

    private String url;

    private String username;

    private String password;

    private ObjectName j2eeServer;

    protected WebSphereServerAction(final ServerBean server) {
        this.name = server.getName();
        this.url = server.getServerConnector().getConnectorUrl();
        this.username = server.getServerConnector().getUsername();
        this.password = server.getServerConnector().getPassword();
        this.appendToLog("Created WebSphereServerAction for server '" + this.name + "'");
    }

    @Override
    @SuppressWarnings("unchecked")
    protected void connectViaJMX() throws Exception {
        String root = System.getenv(WebSphereServerAction.WEBSPHERE_ROOT);
        if (root == null) {
            root = System.getProperty(WebSphereServerAction.WEBSPHERE_ROOT);
        }

        List<URL> files = new ArrayList<URL>();
        if (root != null) {
            File runtimes = new File(root, "runtimes");
            for (File file : runtimes.listFiles()) {
                if (file.getName().startsWith("com.ibm.ws.admin.client")) {
                    files.add(file.toURI().toURL());
                } else if (file.getName().startsWith("com.ibm.ws.orb")) {
                    files.add(file.toURI().toURL());
                }
            }
        }

        if (files.size() == 0) {
            throw new Exception("You need to set the environment variable " + WebSphereServerAction.WEBSPHERE_ROOT
                + " to a valid license in order to connect to a WebSphere server.\nCurrently, "
                + WebSphereServerAction.WEBSPHERE_ROOT + " is set to \"" + root + "\"");
        }

        URL[] urls = new URL[files.size()];
        urls = files.toArray(urls);
        URLClassLoader cl = new URLClassLoader(urls, this.getClass().getClassLoader());
        ClassLoader oldCtxCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(cl);

            Map<String, Object> env = new HashMap<String, Object>();
            if (this.username != null && this.password != null) {
                env.put(Context.SECURITY_PRINCIPAL, this.username);
                env.put(Context.SECURITY_CREDENTIALS, this.password);
            }
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");

            this.establishJMXConnection(new JMXServiceURL(this.url), env);
            this.j2eeServer = ((Set<ObjectName>) this.mbscnx.queryNames(new ObjectName(
                "WebSphere:j2eeType=J2EEServer,type=Server,*"), null)).iterator().next();
        } finally {
            Thread.currentThread().setContextClassLoader(oldCtxCL);
        }

        this.appendToLog("JMX server connection OK for server '" + this.name + "', J2EEServer is '" + this.j2eeServer + "'");
    }

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

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

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

    @Override
    @SuppressWarnings("unchecked")
    public boolean enableOrDisableApplications(final boolean enable) throws Exception {
        this.checkJMXConnection();
        this.appendToLog("Enabling or disabling all applications on this server");

        boolean failed = false;

        for (ObjectName app : (Set<ObjectName>) this.mbscnx.queryNames(new ObjectName("WebSphere:type=SessionManager,*"), null)) {
            String path = app.getKeyProperty("mbeanIdentifier");
            if (path != null) {
                path = path.substring(path.indexOf("/"));
            }
            ObjectName filter;
            try {
                filter = ((Set<ObjectName>) this.mbscnx.queryNames(new ObjectName("WebSphere:type=J2EEFilter,path=" + path),
                    null)).iterator().next();
            } catch (NoSuchElementException e) {
                this.appendToLog("\tApplication on path '" + path
                    + "' doesn't have the OnlyAllowUsersWithSessionFilter filter! Please "
                    + "import the filter JAR from the jadort-samples package and set it in the application's descriptor");
                failed = true;
                continue;
            }

            this.mbscnx.setAttribute(filter, new Attribute("active", Boolean.valueOf(!enable)));
            this.appendToLog("\tFilter for application on path '" + path + "' has been set as " + (enable ? "in" : "")
                + "active, application is therefore " + (enable ? "enabled" : "disabled"));
        }
        if (failed) {
            this.appendToLog("At least one application on this server couldn't be " + (enable ? "enabled" : "disabled"));
            return false;
        } else {
            this.appendToLog("All applications on this server are now " + (enable ? "enabled" : "disabled"));
            return true;
        }
    }

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

    @Override
    public int getActiveSessions(final String appName) throws Exception {
        throw new UnsupportedOperationException("Operation not implemented yet !");
    }

    @Override
    @SuppressWarnings("unchecked")
    public int getActiveSessions() throws Exception {
        Long totalActiveSessions = new Long(0);

        if (this.mbscnx != null) {
            this.checkJMXConnection();

            Set<ObjectName> managers = this.mbscnx.queryNames(new ObjectName("WebSphere:type=SessionManager,*"), null);
            for (ObjectName manager : managers) {
                Object stats = this.mbscnx.getAttribute(manager, "stats");
                Class[] paramTypes = {String.class};
                Method m = stats.getClass().getMethod("getStatistic", paramTypes);
                Object liveCount = m.invoke(stats, "LiveCount");
                m = liveCount.getClass().getMethod("getCurrent");
                totalActiveSessions += (Long) m.invoke(liveCount);
            }
        }

        return totalActiveSessions.intValue();
    }

    @Override
    public ApplicationBean getApplicationBean(final String appName) throws Exception {
        throw new UnsupportedOperationException("Operation not implemented yet !");
    }

    @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>();
        Set<ObjectName> managers = this.mbscnx.queryNames(new ObjectName("WebSphere:type=SessionManager,*"), null);
        for (ObjectName manager : managers) {
            String name = manager.getKeyProperty("mbeanIdentifier");
            if (name != null) {
                name = name.substring(name.indexOf("/") + 1);
                if (name.length() == 0) {
                    name = "[ ROOT ]";
                }
                ApplicationBean applicationBean = new ApplicationBean(name, null);
                applicationBean.setState(ServerAction.STATE_DEPLOYED);
                applications.add(applicationBean);
            }
        }

        return applications;
    }

    @Override
    public String setDefault(final String appName) throws Exception {
        throw new UnsupportedOperationException("Operation not implemented yet !");
    }

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

    @Override
    public void stop() throws Exception {
        this.checkJMXConnection();
        this.appendToLog("Stopping server");

        this.mbscnx.invoke(this.j2eeServer, "stop", null, null);

        this.appendToLog("Server is stopped");
    }

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

    @Override
    public String upload(final ApplicationBean application) throws Exception {
        throw new UnsupportedOperationException("Operation not implemented yet !");
    }
}
