/**
 *==============================================================================
 * Copyright © 2001-2005 by Allesta, LLC. All rights reserved.
 * Copyright (C) 2007 Bull S.A.S.
 *==============================================================================
 *
 * 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 (at your option) 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.webapp.jonasadmin.service.webservice.provider;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.ObjectName;
import javax.xml.namespace.QName;

import org.ow2.jonas.lib.management.extensions.base.JonasManagementRepr;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.webapp.jonasadmin.service.webservice.provider.element.HandlerItem;
import org.ow2.jonas.webapp.jonasadmin.service.webservice.provider.element.InitParamItem;
import org.ow2.jonas.webapp.jonasadmin.service.webservice.provider.element.PortComponentItem;
import org.ow2.jonas.webapp.jonasadmin.service.webservice.provider.element.ServiceImplBean;
import org.ow2.jonas.webapp.jonasadmin.service.webservice.provider.element.WebServiceDescriptionItem;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * Jonas specific provider meant to extract useful information from mbeans
 * registered in jonas and fill specific constructs.
 *
 * @author Vivek Lakshmanan
 */
public class JOnASProvider {

    public static final String ON_STATELESS_SESSION_BEANS = "*:j2eeType=StatelessSessionBean,*";

    public static final String ON_J2EE_SERVERS = "*:j2eeType=J2EEServer,*";

    public static final String ON_WEB_SERVICES = "*:type=WebService,*";

    private List webServicesDescriptions = new ArrayList();

    private String serverName;

    protected static Logger logger = Log.getLogger(Log.JONAS_ADMIN_PREFIX);

    public JOnASProvider(String serverName) {
        this.serverName = serverName;
    }

    /**
     * Get webservice descriptions for webservices registered on jonas.
     *
     * @return Collection of web service descriptions.
     * @throws Exception When the mbean server could not be queried.
     */
    public Collection getWebServiceDescription() throws Exception {
        reload();
        return webServicesDescriptions;

    }

    /**
     * Creates a list of handlers for the portcomponent based on the array of
     * object names (converted to strings) passed in here.
     *
     * @param handlerONList
     *            Array of object names converted to strings representing
     *            JAX-RPC handlers instead of AXIS handlers.
     * @return Collection of Handlers
     * @throws Exception
     *             when any of the strings passed in fail to be converted to
     *             equivalent Object Names.
     */
    private Collection createHandlerList(String[] handlerONList) throws Exception {

        Collection handlerList = new ArrayList();
        for (int i = 0; i < handlerONList.length; i++) {
            ObjectName handlerON;
            handlerON = new ObjectName(handlerONList[i]);
            String[] paramList = new String[] { "name", "classname", "soapHeaders", "soapRoles", "initParams" };
            AttributeList attrList = null;

            attrList = JonasManagementRepr.getAttributes(handlerON, paramList, serverName);
            // Convert result to a hashmap to make it more readable.
            Map attrs = convertToHashMap(attrList);
            HandlerItem handler = new HandlerItem((String) attrs.get("name"), (String) attrs.get("classname"));

            String[] soapHeaders = (String[]) attrs.get("soapHeaders");
            for (int j = 0; j < soapHeaders.length; j++) {
                handler.setSoapHeader(j, new QName(soapHeaders[j]));
            }

            String[] soapRoles = (String[]) attrs.get("soapRoles");
            for (int j = 0; j < soapRoles.length; j++) {
                handler.setSoapRole(j, soapRoles[j]);
            }

            Properties initParams = (Properties) attrs.get("initParams");
            Iterator it = initParams.keySet().iterator();
            int counter = 0;
            while (it.hasNext()) {
                String key = (String) it.next();
                handler.setInitParam(counter, new InitParamItem(key, initParams.getProperty(key)));
                counter++;
            }
            handlerList.add(handler);
        }
        return handlerList;
    }

    /**
     * Generates port components based on the array of Object Names in string
     * form passed as a parameter.
     *
     * @param portComponentONList
     *            Array of Object Names as strings representing port components.
     * @return PortComponent objects corresponding to the input.
     * @throws Exception
     *             When any of the object names are not legal.
     */
    private Vector createPortComponentList(String[] portComponentONList) throws Exception {
        Vector pcVector = new Vector();
        // Iterate through list of port component objectnames provided and
        // extract information from the mbeans.
        for (int i = 0; i < portComponentONList.length; i++) {
            ObjectName pcON;
            pcON = new ObjectName(portComponentONList[i]);
            // List of attributes of interest to us.
            String[] paramList = new String[] { "name", "wsdlPort", "endpoint", "implementationBean",
                    "serviceEndpointInterface", "handlers" };
            AttributeList attrList = null;
            attrList = JonasManagementRepr.getAttributes(pcON, paramList, serverName);

            // Convert result to a hashmap to make it more readable.
            Map attrs = convertToHashMap(attrList);
            // Create our model of the service endpoint interface after checking
            // if the service
            // is a servlet based endpoint or a J2EE endpoint.
            ServiceImplBean serviceBean = null;
            serviceBean = new ServiceImplBean(checkServletOrSessionBean((String) attrs
                    .get("implementationBean")), (String) attrs.get("implementationBean"));
            QName wsdlPort = new QName((String) attrs.get("wsdlPort"));
            String endPoint = (String) attrs.get("serviceEndpointInterface");
            String name = (String) attrs.get("name");
            // URL where endpoint can be accessed.
            String url = (String) attrs.get("endpoint");

            PortComponentItem pc = new PortComponentItem(name, wsdlPort, endPoint, serviceBean, url);
            String[] handlers = (String[]) attrs.get("handlers");
            if (handlers != null) {
                Iterator iter = null;
                iter = createHandlerList(handlers).iterator();
                int counter = 0;
                while (iter.hasNext()) {
                    pc.setHandler(counter++, (HandlerItem) iter.next());
                }
            }

            pcVector.add(pc);

        }

        return pcVector;
    }

    /**
     * Checks if the implementation bean represented by the parameter is a
     * session bean or a servlet.
     *
     * @param implBeanON
     *            String version of Object Name of service implementation bean.
     * @return ServiceImplBean.EJB_BEAN if it is a session bean otherwise
     *         returns ServiceImplBean.SERVLET_BEAN if it is a servlet bean.
     * @throws Exception
     *             When the string version of the Object Name does not convert
     *             to a legal Object Name.
     */
    private int checkServletOrSessionBean(String implBeanON) throws Exception {
        ObjectName implBeanOName = new ObjectName(implBeanON);
        // Pattern for a stateless session bean.
        ObjectName isEjbServiceImpl = new ObjectName(ON_STATELESS_SESSION_BEANS);

        if (isEjbServiceImpl.apply(implBeanOName)) {
            // objectname matches pattern for stateless session bean.
            return ServiceImplBean.EJB_BEAN;
        } else {
            // objectname does not match pattern for stateless session bean.
            return ServiceImplBean.SERVLET_BEAN;
        }
    }

    /**
     * Returns the domain name for this server.
     *
     * @return Domain name for the server.
     */
    private String getDomainName() throws Exception {
        ObjectName j2eeServerON = new ObjectName(ON_J2EE_SERVERS);
        String domainName = null;
        Iterator it = JonasManagementRepr.queryNames(j2eeServerON, null).iterator();
        if (it.hasNext()) {
            domainName = ((ObjectName) it.next()).getDomain();
        }
        return domainName;
    }

    /**
     * Converts attribute list to a hashmap keyed with the corresponding entries
     * in the paramList array.
     *
     * @param paramList
     *            The keys for the hashmap which will be returned.
     * @param attrList
     *            The value elements for the hashmap which will be returned.
     * @return A HashMap representing keys from paramList and values from
     *         attrList. The values are converted to their native form.
     */
    private static Map convertToHashMap(AttributeList attrList) {
        Map map = new HashMap();
        for (Iterator i = attrList.iterator(); i.hasNext();) {
            Attribute attr = (Attribute) i.next();
            map.put(attr.getName(), attr.getValue());
        }
        return map;
    }

    /**
     * Gets a specific webservice description based on the given ID.
     *
     * @param id
     *            A unique identifier.
     * @return Corresponding webservice description.
     * @throws Exception
     *             when no webservices found, EntityNotFoundException when the
     *             when the ID did not match a webservice description.
     */
    public WebServiceDescriptionItem getWebServiceDescription(String id) throws Exception {
        reload();

        WebServiceDescriptionItem webServiceDescription = null;

        if (webServicesDescriptions.size() > 0) {
            for (Iterator i = webServicesDescriptions.iterator(); i.hasNext();) {
                WebServiceDescriptionItem wsd = (WebServiceDescriptionItem) i.next();
                if (wsd.getName().equals(id)) {
                    webServiceDescription = wsd;
                    break;
                }
            }
        } else {
            throw new Exception("Web Services list is null");
        }

        if (webServiceDescription == null) {
            throw new Exception("No Web Service Description found with name=" + id);
        }

        return webServiceDescription;
    }

    /**
     * Reloads information webservice mbeans in jonas into the wsabi4j2ee
     * specific constructs.
     *
     * @throws Exception
     */
    private void reload() throws Exception {
        ObjectName wsDescriptionON = null;
        wsDescriptionON = new ObjectName(ON_WEB_SERVICES);

        webServicesDescriptions.clear();
        Iterator wsDescIter = null;
        wsDescIter = JonasManagementRepr.queryNames(wsDescriptionON, serverName).iterator();
        while (wsDescIter.hasNext()) {
            ObjectName wsDescIterON = (ObjectName) wsDescIter.next();
            String[] attributes = { "name", "wsdlFilename", "mappingFilename", "portComponents", "wsdlURL" };
            AttributeList attrList = null;
            attrList = JonasManagementRepr.getAttributes(wsDescIterON, attributes, serverName);
            Map attrMap = convertToHashMap(attrList);

            // Extract port components from attrList.
            String[] pcList = (String[]) attrMap.get("portComponents");
            String name = (String) attrMap.get("name");
            Vector portComponents = null;
            // Create new port components.
            portComponents = createPortComponentList(pcList);
            // Create a new web service description.
            WebServiceDescriptionItem wsd = new WebServiceDescriptionItem(name, (String) attrMap
                    .get("wsdlFilename"), (String) attrMap.get("wsdlURL"), (String) attrMap
                    .get("mappingFilename"), portComponents);
            webServicesDescriptions.add(wsd);
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Web service description added: " + wsd);
            }

        }

    }

}
