/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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 com.queplix.core.modules.eql.ejb;

import com.queplix.core.integrator.security.AccessRightsManager;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.modules.services.utils.log.impl.MailLogPublisher;
import com.queplix.core.utils.async.ASyncObject;
import com.queplix.core.utils.async.ASyncRequest;
import com.queplix.core.utils.log.AbstractLogger;
import com.queplix.core.utils.log.Log;

import javax.ejb.EJBException;
import javax.ejb.MessageDrivenBean;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.transaction.UserTransaction;
import java.util.HashMap;

/**
 * Message Driven Bean for working with asynchronous requests.
 *
 * @author [BAA] Alexander Balandin
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @author [ALB] Baranov Andrey
 */

public class AsyncMDB
        implements MessageDrivenBean, MessageListener {

    // ================================================================== Constants

    public static final String OBJECT_PARAM = "object";
    public static final String REQUEST_PARAM = "request";

    // ================================================================== Fields

    /**
     * Runtime MDB context reference.
     */
    protected MessageDrivenContext ctx = null;

    /**
     * Logger.
     */
    private AbstractLogger logger = null;

    // ============================================================= API methods

    /**
     * Sets MDB context.
     *
     * @param ctx MDB context
     */
    public void setMessageDrivenContext(MessageDrivenContext ctx) {
        this.ctx = ctx;
    }

    /**
     * Creates MDB.
     */
    public void ejbCreate() {
        logger = Log.getLog(getClass());
        logger.INFO("[+] AsyncMDB [" + hashCode() + "] created");
    }

    /**
     * Remove MDB.
     */
    public void ejbRemove() {
        logger.INFO("[-] AsyncMDB [" + hashCode() + "] removed");
    }

    /**
     * Gets a message.
     *
     * @param msg message
     */
    public void onMessage(Message msg) {

        boolean error = false;
        UserTransaction userTran = ctx.getUserTransaction();

        // Go!
        try {

            // Message type checking.
            if(!(msg instanceof ObjectMessage)) {
                logger.ERROR("AsyncMDB [" + hashCode()
                        + "] got unknown message: " + msg);
                return;
            }

            if(logger.getLogger().isDebugEnabled()) {
                logger.DEBUG(
                        "AsyncMDB [" + hashCode() + "] got message: " + msg);
            }

            //
            // Retriving HashMap with message data.
            //
            Object o = ((ObjectMessage) msg).getObject();
            if(o == null || !(o instanceof HashMap)) {
                logger.ERROR("Can't get data hash from message: " + msg);
                return;
            }
            HashMap map = (HashMap) o;

            //
            // Getting the ASyncObject object.
            //
            ASyncObject obj;
            o = map.get(OBJECT_PARAM);
            if(o != null) {
                logger.DEBUG("Async object:");
                logger.DEBUG("		class: " + o.getClass().getName());
                logger.DEBUG("		object: " + o);
                obj = (ASyncObject) o;
            } else {
                logger.ERROR("Can't get 'OBJECT_PARAM' from HashMap: " + map);
                return;
            }

            //
            // Getting the request object.
            //
            ASyncRequest request = null;
            o = map.get(REQUEST_PARAM);
            if(o != null) {
                logger.DEBUG(
                        "Request object class name: " + o.getClass().getName());
                request = (ASyncRequest) o;
            }

            // Begin the transaction.
            userTran.begin();

            //
            // Processing request.
            //
            obj.process(request);

            // Commit.
            userTran.commit();

        } catch (JMSException ex) {

            //
            // Caught JMS exception.
            //
            error = true;
            try {
                userTran.rollback();
            } catch (Throwable t) {
                traceError(t);
            }

            // Tracing error.
            traceError(ex);

        } catch (Exception ex) {

            //
            // Caught unknown exception.
            //
            error = true;
            try {
                userTran.rollback();
            } catch (Throwable t) {
                traceError(t);
            }

            // Tracing error.
            traceError(ex);
        }

        if(logger.getLogger().isDebugEnabled()) {
            if(error) {
                logger.DEBUG(
                        "AsyncMDB [" + hashCode() + "] finished with error");
            } else {
                logger.DEBUG(
                        "AsyncMDB [" + hashCode() + "] finished successfully");
            }
        }

    } // onMessage()

    // ============================================================= Private methods

    //
    // Trace error.
    // Send error message from admin.
    //

    private void traceError(Throwable t) {

        // Tracing error.
        logger.ERROR("=================================");
        logger.ERROR("Caught exception in Async MDB: " + t);
        logger.ERROR("Stack:");
        logger.ERROR(t);
        logger.ERROR("=================================");

        try {
            // Get Admin logon session.
            LogonSession ls = AccessRightsManager.getSystemLogonSession();

            // Sending mail.
            new MailLogPublisher(ls, getClass()).ERROR(getCauseException(t));

        } catch (Throwable t2) {
            t2.printStackTrace();
        }
    }

    //
    // Try to find cause exception.
    //
    private Throwable getCauseException(Throwable t) {

        if(t == null) {
            return null;
        }

        // if EJB exception - takes cause exception
        if((t instanceof EJBException)
                && ((EJBException) t).getCausedByException() != null) {
            return getCauseException(((EJBException) t).getCausedByException());
        } else {
            return t;
        }
    }
}
