/**
 * Copyright (c) 2006-2012 EBM WebSourcing, 2012-2015 Linagora
 * 
 * This program/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 program/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 program/library; If not, see <http://www.gnu.org/licenses/>
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.se.rmi;

import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;

import javax.jbi.JBIException;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.servicedesc.ServiceEndpoint;

import org.objectweb.petals.tools.rmi.server.remote.implementations.RemoteComponentContextImpl;
import org.objectweb.petals.tools.rmi.server.remote.implementations.RemoteDeliveryChannelImpl;
import org.objectweb.petals.tools.rmi.server.remote.interfaces.RemoteComponentContext;
import org.objectweb.petals.tools.rmi.server.remote.interfaces.RemoteDeliveryChannel;
import org.ow2.petals.component.framework.bc.AbstractBindingComponent;
import org.ow2.petals.component.framework.process.JBIAcceptorManager;
import org.ow2.petals.component.framework.process.JBIProcessorManager;
import org.ow2.petals.se.rmi.registry.Registry;
import org.ow2.petals.se.rmi.registry.RmiProperties;
import org.ow2.petals.se.rmi.registry.RmiPropertiesException;
import org.w3c.dom.Document;

/**
 * Class FrontEngine.java.
 * @author Nicolas Salatge - EBM WebSourcing
 * @author Roland NAUDIN - EBM WebSourcing
 */
public class RmiComponent extends AbstractBindingComponent {

    /**
     * the RMI registry Server.
     */
    private Registry rmiServer;

    /**
     * the RMI component context.
     */
    private RemoteComponentContextImpl rmiComponentContext = null;

    /**
     * the RMI delivery channel.
     */
    private RemoteDeliveryChannel rmiDeliveryChannel = null;

    /**
     * HasMap containing all the messages exchanges.
     */
    private Map<String, MessageExchange> messages;

    /**
     * The RMI properties.
     */
    private RmiProperties rmiProperties = null;

    /**
     * get messaging.
     * 
     * @return the messages
     */
    public Map<String, MessageExchange> getMessages() {
        return this.messages;
    }

    /**
     * Get the RMI component context.
     * 
     * @return the RMI component context
     */
    public RemoteComponentContext getRmiComponentContext() {
        return this.rmiComponentContext;
    }

    /**
     * get the rmi delivery channel.
     * 
     * @return the rmi delivery channel
     */
    public RemoteDeliveryChannel getRmiDeliveryChannel() {
        return this.rmiDeliveryChannel;
    }

    /**
     * Get the description of service.
     * 
     * @param endpoint
     *            the endpoint
     * @return the description
     */
    public Document getServiceDescription(final ServiceEndpoint endpoint) {
        return this.rmiComponentContext.getOwnedEndpointDescriptor(endpoint.getEndpointName());
    }

    /**
     * Init the component.
     * 
     * @param context
     *            the context
     * @throws JBIException
     *             impossible to init the component
     */
    public void doInit() throws JBIException {

        try {
        	
            this.rmiProperties = new RmiProperties(this.getComponentExtensions());

            this.messages = new ConcurrentHashMap<String, MessageExchange>();
            
			this.rmiDeliveryChannel = new RemoteDeliveryChannelImpl(this.getChannel(),
			        this.getContext(), this.getMessages(), this.getLogger());

	        this.rmiComponentContext = new RemoteComponentContextImpl(this.getContext(),
	                this.getRmiDeliveryChannel());
	        
	        if(this.rmiServer == null) {
	        	this.rmiServer = new Registry(this.rmiProperties);
	        }

	        this.rmiServer.initializeRegistry();
		} catch (final RemoteException e) {
			throw new JBIException("Failed to init RMI component", e);
        } catch (final RmiPropertiesException e) {
            throw new JBIException("Failed to retrieve the RMI properties from the component descriptor file");
        }
    }

    /**
     * start the component.
     * 
     * @throws JBIException
     *             impossible to start the component.
     */
    public void doStart() throws JBIException {
        this.getLogger().info("Starting RMI Service Engine...");
        this.getLogger().info(" - RMI Port : " + this.rmiProperties.getRmiPort());
        this.getLogger().info(" - Embedded : " + this.rmiProperties.isEmbeddedRegistry());
        this.getLogger().info(" - Component Context Name : " + this.rmiProperties.getComponentContextName());

        try {
            // The Petals SE RMI manage itself messages received on the delivery channel.
            // MessageExchangeAcceptors and MessageExchangeProcessors must be stopped.
            final Class<?> abstractBindingComponentClass = this.getClass().getSuperclass();
            final Class<?> abstractComponentClass = abstractBindingComponentClass.getSuperclass();
            final Field acceptorManagerField = abstractComponentClass.getDeclaredField("acceptorManager");
            acceptorManagerField.setAccessible(true);
            final JBIAcceptorManager acceptorManager = (JBIAcceptorManager) acceptorManagerField.get(this);
            acceptorManager.stop();

            final Field processorManagerField = abstractComponentClass.getDeclaredField("processorManager");
            processorManagerField.setAccessible(true);
            final JBIProcessorManager processorManager = (JBIProcessorManager) processorManagerField.get(this);
            processorManager.stop();


            this.rmiServer.registerRMIInterface(this.rmiComponentContext);

        } catch (RemoteException e) {
            this.getLogger().severe(e.getClass().getSimpleName() + " - " + e.getMessage());
            throw new JBIException(e);
        } catch (MalformedURLException e) {
            this.getLogger().severe(e.getClass().getSimpleName() + " - " + e.getMessage());
            throw new JBIException(e);
        } catch (AlreadyBoundException e) {
            this.getLogger().severe(e.getClass().getSimpleName() + " - " + e.getMessage());
            throw new JBIException(e);
        } catch (final SecurityException e) {
            this.getLogger().severe(e.getClass().getSimpleName() + " - " + e.getMessage());
            throw new JBIException(e);
        } catch (final NoSuchFieldException e) {
            this.getLogger().severe(e.getClass().getSimpleName() + " - " + e.getMessage());
            throw new JBIException(e);
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * stop the component.
     * 
     * @throws JBIException
     *             impossible to start the component.
     */
    public void doStop() throws JBIException {
        try {
            this.messages.clear();
            this.rmiServer.deregisterRMIInterface();
        } catch (RemoteException e) {
            this.getLogger().log(Level.WARNING, e.getMessage(), e);
        } catch (MalformedURLException e) {
            this.getLogger().log(Level.SEVERE, e.getMessage(), e);
            throw new JBIException(e.getMessage());
        } catch (NotBoundException e) {
            this.getLogger().log(Level.SEVERE, e.getMessage(), e);
            throw new JBIException(e.getMessage());
        }
    }

    /**
     * This function allows to try to shutdown RMI component
     * 
     * @throws JBIException
     *             unexpected event occurred during shutdown component process.
     */
    public void doShutDown() throws JBIException {
        try {
            this.rmiServer.shutDown(this.rmiComponentContext);
        } catch (final AccessException e) {
            throw new JBIException("Failed to shutdown RMI registry", e);
        } catch (final RemoteException e) {
            throw new JBIException("Failed to shutdown RMI registry", e);
        } catch (final NotBoundException e) {
            throw new JBIException("Failed to shutdown RMI registry", e);
        }
        this.getLogger().log(Level.INFO, "RMI registry correctly shut down");
    }
}
