/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.ode.bpel.engine;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ReflectionException;

import org.apache.ode.bpel.iapi.BpelServer;
import org.apache.ode.bpel.iapi.ProcessStore;


/**
 * Standard MBean exposing ODE's process model and instance management API
 *
 * @author Tammo van Lessen (University of Stuttgart)
 */
public class ProcessAndInstanceManagementMBean implements DynamicMBean {

    private MBeanInfo _mbeanInfo;
    private ProcessAndInstanceManagementImpl _pm;

    private static final List<String> __excludes = new ArrayList<String>();
        static {
        __excludes.add("hashCode");
        __excludes.add("equals");
        __excludes.add("getClass");
        __excludes.add("wait");
        __excludes.add("notify");
        __excludes.add("notifyAll");
        __excludes.add("toString");
    }

    private final static Hashtable<String, Class<?>> primitives = new Hashtable<String, Class<?>>();
    static {
        primitives.put(Boolean.TYPE.toString(), Boolean.TYPE);
        primitives.put(Character.TYPE.toString(), Character.TYPE);
        primitives.put(Byte.TYPE.toString(), Byte.TYPE);
        primitives.put(Short.TYPE.toString(), Short.TYPE);
        primitives.put(Integer.TYPE.toString(), Integer.TYPE);
        primitives.put(Long.TYPE.toString(), Long.TYPE);
        primitives.put(Float.TYPE.toString(), Float.TYPE);
        primitives.put(Double.TYPE.toString(), Double.TYPE);
    }

    public ProcessAndInstanceManagementMBean(BpelServer server, ProcessStore store) {
        this(new ProcessAndInstanceManagementImpl(server, store));
    }

    /**
     */
    public ProcessAndInstanceManagementMBean(ProcessAndInstanceManagementImpl pm) {
        _pm = pm;
        List<MBeanOperationInfo> exposedOperations = new ArrayList<MBeanOperationInfo>();
        for (Method m : pm.getClass().getMethods()) {
            if (!__excludes.contains(m.getName())) {
                exposedOperations.add(new MBeanOperationInfo(m.getName(), m));
            }
        }

        MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[] {};
        MBeanConstructorInfo[] constructors = new MBeanConstructorInfo[] {};
        MBeanOperationInfo[] operations = new MBeanOperationInfo[exposedOperations.size()];
        operations = (MBeanOperationInfo[]) exposedOperations.toArray(operations);
        MBeanNotificationInfo[] notifications = new MBeanNotificationInfo[] {};

        _mbeanInfo = new MBeanInfo(getClass().getName(), "Process and Instance Management",
                attributes, constructors, operations, notifications);
    }

    public Object getAttribute(String attribute)
            throws AttributeNotFoundException, MBeanException,
            ReflectionException {
        throw new UnsupportedOperationException();
    }

    public AttributeList getAttributes(String[] attributes) {
        throw new UnsupportedOperationException();
    }

    public MBeanInfo getMBeanInfo() {
        return _mbeanInfo;
    }

	public Object invoke(String actionName, Object[] params, String[] signature)
			throws MBeanException, ReflectionException {
		ClassLoader cl = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
			Method m = _pm.getClass().getMethod(actionName, findTypes(_pm.getClass().getClassLoader(), signature));
			if (m == null) {
				throw new ReflectionException(new NoSuchMethodException(actionName));
			}
			return m.invoke(_pm, params);
		} catch (Exception e) {
			throw new ReflectionException(e);
		} finally {
            Thread.currentThread().setContextClassLoader(cl);
        }
    }

    private Class[] findTypes(ClassLoader loader, String[] signature) throws ReflectionException {
        if (signature == null)
            return null;
        final Class[] result = new Class[signature.length];
        try {
            for (int i = 0; i < signature.length; i++) {
                result[i] = primitives.get(signature[i]);
                if (result[i] == null) {
                    result[i] = Class.forName(signature[i], false, loader);
                }
            }
        } catch (ClassNotFoundException e) {
            throw new ReflectionException(e);
        }
        return result;
    }

    public void setAttribute(Attribute attribute)
            throws AttributeNotFoundException, InvalidAttributeValueException,
            MBeanException, ReflectionException {
        throw new UnsupportedOperationException();
    }

    public AttributeList setAttributes(AttributeList attributes) {
        throw new UnsupportedOperationException();
    }

}
