/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     cdelahun - Bug 214534: added JMS Cache Coordination for publishing only
package org.eclipse.persistence.sessions.coordination.jms;

import java.util.Map;

import org.eclipse.persistence.exceptions.RemoteCommandManagerException;
import org.eclipse.persistence.internal.sessions.coordination.RemoteConnection;
import org.eclipse.persistence.internal.sessions.coordination.jms.JMSTopicRemoteConnection;
import org.eclipse.persistence.sessions.coordination.RemoteCommandManager;

/**
 * <p>
 * <b>Purpose</b>: Provide a transport implementation for the Remote Command Module (RCM) that both publishes
 * and subscribes to a JMS topic.
 * <p>
 * <b>Description</b>: This class manages two connections to the same known JMS Topic:
 * external connection for publishing, local connection for receiving messages.
 * <p>
 * @author Steven Vo
 * @since OracleAS TopLink 10<i>g</i> (10.0.3)
 */
public class JMSTopicTransportManager extends JMSPublishingTransportManager {

    public JMSTopicTransportManager(RemoteCommandManager rcm) {
        super(rcm);
    }

    /**
     * INTERNAL:
     * JMSTopicTransportManager may have only two connections: one local and one external.
     * In case the local connection doesn't exist, this method creates it.
     */
    public synchronized void createLocalConnection() {
        if(localConnection == null) {
            try {
                localConnection = createConnection(true);
            } catch (RemoteCommandManagerException rcmException) {
                // to recover handle RemoteCommandManagerException.ERROR_CREATING_LOCAL_JMS_CONNECTION:
                // after changing something (for instance jmsHostUrl)
                // call createLocalConnection method again.
                rcm.handleException(rcmException);
            }
        }
    }

    /**
     * INTERNAL:
     * In case there's no external connection attempts to create one,
     * if that's successful then (in case there is no local connection, too)
     * attempts to create local connection in a separate thread.
     * Returns clone of the original map.
     */
    public Map<String, RemoteConnection> getConnectionsToExternalServicesForCommandPropagation() {
        if (this.localConnection == null && !this.rcm.isStopped()) {
            // It's a good time to create localConnection,
            // in a new thread - to return externalConnections promptly.
            this.rcm.getServerPlatform().launchContainerRunnable(new Runnable() {
                public void run() {
                    try {
                        createLocalConnection();
                    } catch (RemoteCommandManagerException ex) {
                        // Ignore exception - user had a chance to handle it in createLocalConnection method:
                        // for instance to change host url and create a new local connection.
                    }
                }
            });
        }
        return super.getConnectionsToExternalServicesForCommandPropagation();
    }

    /**
     * INTERNAL:
     * caches local connection, set localConnection to null, closes the cached connection in a new thread.
     */
    public void removeLocalConnection() {
        JMSTopicRemoteConnection connectionToRemove = (JMSTopicRemoteConnection)localConnection;
        synchronized(this) {
            if(connectionToRemove == localConnection) {
                localConnection = null;
            } else {
                connectionToRemove = null;
            }
        }
        // closing connection may take time - do it outside of the synchronized block
        if(connectionToRemove != null) {
            connectionToRemove.close();
        }
    }
}
