/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2012 Bull S.A.S.
 * Contact: jonas-team@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 (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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.lib.tenant.listener;

import org.ow2.easybeans.api.event.bean.EZBMessageDrivenInfo;
import org.ow2.easybeans.api.event.naming.EZBJavaContextNamingEvent;
import org.ow2.jonas.lib.tenant.context.TenantContext;
import org.ow2.jonas.lib.tenant.context.TenantCurrent;
import org.ow2.util.ee.metadata.ejbjar.api.struct.IActivationConfigProperty;
import org.ow2.util.event.api.EventPriority;
import org.ow2.util.event.api.IEvent;
import org.ow2.util.event.api.IEventListener;

import javax.naming.Context;
import javax.naming.NamingException;
import java.util.List;

/**
 * Handle events
 * @author Mohammed Boukada
 */
public class TenantEventListener implements IEventListener {

    /**
     * Separator between tenant-id prefix and JNDI name
     */
    private String separator;

    /**
     * ActivationConfigProperty : destination property name
     */
    private final String DESTINATION_PROPERTY_NAME = "destination";

    /**
     * Constructor
     * @param separator
     */
    public TenantEventListener(String separator) {
        this.separator = separator;
    }

    /**
     * Handle the event.
     * @param event The event to handle.
     */
    public void handle(IEvent event) {
        if (event instanceof EZBMessageDrivenInfo) {
            // Customize MDB destination property
            EZBMessageDrivenInfo mEvent = (EZBMessageDrivenInfo) event;
            List<IActivationConfigProperty> properties = mEvent.getActivationConfigProperties();
            for (IActivationConfigProperty property : properties) {
                if (DESTINATION_PROPERTY_NAME.equals(property.propertyName())) {
                    String oldValue = property.propertyValue();
                    String tenanId = getTenantId();
                    if (tenanId != null && !TenantContext.DEFAULT_TENANT_ID.equals(tenanId)) {
                        property.setPropertyValue(tenanId + separator + oldValue);
                    }
                }
            }
        } else if (event instanceof EZBJavaContextNamingEvent) {
            // Bind tenantId for EJB JNDI context
            EZBJavaContextNamingEvent ene = (EZBJavaContextNamingEvent) event;
            Context javaContext = ene.getJavaContext();
            String tenantId = getTenantId();
            try {
                if (tenantId != null) {
                    javaContext.rebind("comp/tenantId", getTenantId());
                }
            } catch (NamingException e) {
                throwException(ene, new IllegalStateException("Cannot lookup java:comp element.", e));
            }
        }
    }

    /**
     * Check whether the listener wants to handle this event.
     * @param event The event to check.
     * @return True if the listener wants to handle this event, false otherwise.
     */
    public boolean accept(IEvent event) {
        if (event instanceof EZBMessageDrivenInfo) {
            EZBMessageDrivenInfo mEvent = (EZBMessageDrivenInfo) event;
            return !mEvent.getActivationConfigProperties().isEmpty();
        } else if (event instanceof EZBJavaContextNamingEvent) {
            EZBJavaContextNamingEvent javaContextNamingEvent = (EZBJavaContextNamingEvent) event;

            // source/event-provider-id attribute is used to filter the destination
            if ("java:".equals(javaContextNamingEvent.getEventProviderId())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Get the listener priority.
     * @return The listener priority.
     */
    public EventPriority getPriority() {
        return EventPriority.SYNC_NORM;
    }

    /**
     * Gets tenant-id from context
     * @return tenant-id
     */
    private String getTenantId() {
        String tenantId = null;
        if (TenantCurrent.getCurrent().getTenantContext() != null) {
            tenantId = TenantCurrent.getCurrent().getTenantContext().getTenantId();
        }
        return tenantId;
    }

    /**
     * Add the Exception in the event for a possible feedback for the caller.
     * @param event the event to be completed
     * @param throwable the exception to be added and then rethrown
     */
    private static void throwException(final EZBJavaContextNamingEvent event,
                                       final Throwable throwable) {

        // Append the exception in the event
        event.addThrowable(throwable);

        // Rethrow the exception to break the execution flow
        if (throwable instanceof RuntimeException) {
            throw (RuntimeException) throwable;
        } else {
            throw new RuntimeException("Wrapping cause", throwable);
        }
    }
}
