/**
 * 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: WebLogicServerAction.java 3808 2009-06-03 11:55:00Z alitokmen $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.service.action;

import java.io.File;
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.JMXConnectorFactory;
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 WebLogic server.
 * 
 * @author Malek Chahine
 * @author Remy Bresson
 * @author S. Ali Tokmen
 */
public class WebLogicServerAction extends ServerAction {

    private static final String WEBLOGIC_ROOT = "WL_HOME";

    private String name;

    private String url;

    private String username;

    private String password;

    private ObjectName j2eeServer;

    protected WebLogicServerAction(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 WeblogicServerAction for server '" + this.name + "'");
    }

    @Override
    @SuppressWarnings("unchecked")
    protected void connectViaJMX() throws Exception {
        String root = System.getenv(WebLogicServerAction.WEBLOGIC_ROOT);
        if (root == null) {
            root = System.getProperty(WebLogicServerAction.WEBLOGIC_ROOT);
        }
        File wlJMXClientJAR = null;
        if (root != null) {
            wlJMXClientJAR = new File(root, "lib/wljmxclient.jar");
            if (!wlJMXClientJAR.isFile()) {
                wlJMXClientJAR = null;
            }
        }

        if (!this.url.contains("iiop:") && wlJMXClientJAR == null) {
            throw new Exception("You need to set the environment variable " + WebLogicServerAction.WEBLOGIC_ROOT
                + " to a valid license in order to connect to a WebLogic server with proprietary protocols.\nCurrently, "
                + WebLogicServerAction.WEBLOGIC_ROOT + " is set to \"" + root + "\"");
        }

        URLClassLoader cl = null;
        if (wlJMXClientJAR != null) {
            cl = new URLClassLoader(new URL[] {wlJMXClientJAR.toURI().toURL()}, this.getClass().getClassLoader());
        }
        ClassLoader oldCtxCL = Thread.currentThread().getContextClassLoader();
        try {
            if (cl != null) {
                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);
            }

            if (wlJMXClientJAR != null) {
                env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote");
            }

            this.establishJMXConnection(new JMXServiceURL(this.url), env);
            this.j2eeServer = ((Set<ObjectName>) this.mbscnx.queryNames(new ObjectName("*:Type=ServerRuntime,*"), 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("*:Type=WebAppComponentRuntime,*"), null)) {
            String path = (String) this.mbscnx.getAttribute(app, "ContextRoot");
            if (path == null || path.length() < 1) {
                path = "/";
            }
            ObjectName filter;
            try {
                filter = ((Set<ObjectName>) this.mbscnx.queryNames(new ObjectName("*: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 {
        int totalActiveSessions = 0;

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

            for (ObjectName webAppComponentRuntime : (Set<ObjectName>) this.mbscnx.queryNames(new ObjectName(
                "*:Type=WebAppComponentRuntime,*"), null)) {
                totalActiveSessions += ((Integer) this.mbscnx.getAttribute(webAppComponentRuntime, "OpenSessionsCurrentCount"))
                    .intValue();
            }
        }

        return totalActiveSessions;
    }

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

        List<ApplicationBean> applications = new ArrayList<ApplicationBean>();
        for (ObjectName app : (Set<ObjectName>) this.mbscnx.queryNames(new ObjectName("*:Type=WebAppComponentRuntime,*"), null)) {

            String name = (String) this.mbscnx.getAttribute(app, "ModuleId");

            if (name != null) {
                if (name.length() == 0) {
                    name = "[ ROOT ]";
                }
                ApplicationBean applicationBean = new ApplicationBean(name, null);
                applicationBean.setState(ServerAction.STATE_DEPLOYED);
                applications.add(applicationBean);
            }
        }

        return applications;
    }

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

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

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

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

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

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

        this.mbscnx.invoke(this.j2eeServer, "shutdown", 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 !");
    }

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

}
