BaseSessionHandlingStrategy.java

package fr.sii.ogham.sms.sender.impl.cloudhopper.session;

import static fr.sii.ogham.core.retry.NamedCallable.named;

import org.slf4j.Logger;

import com.cloudhopper.smpp.SmppClient;
import com.cloudhopper.smpp.SmppSession;

import fr.sii.ogham.core.exception.retry.MaximumAttemptsReachedException;
import fr.sii.ogham.core.exception.retry.RetryException;
import fr.sii.ogham.core.exception.retry.RetryExecutionInterruptedException;
import fr.sii.ogham.core.retry.RetryExecutor;
import fr.sii.ogham.sms.builder.cloudhopper.SmppClientSupplier;
import fr.sii.ogham.sms.builder.cloudhopper.SmppSessionHandlerSupplier;
import fr.sii.ogham.sms.sender.impl.cloudhopper.ExtendedSmppSessionConfiguration;
import fr.sii.ogham.sms.sender.impl.cloudhopper.exception.ConnectionFailedException;
import fr.sii.ogham.sms.sender.impl.cloudhopper.exception.SmppException;

/**
 * Base class for different session handling strategies.
 * 
 * <p>
 * It provides useful methods to initialize a client and a session. It provides
 * a method to connect (bind) to the SMSC with retry handling. It also provides
 * method to cleanup. The implementations can then use this methods as they
 * wish.
 * 
 * @author Aurélien Baudet
 *
 */
public abstract class BaseSessionHandlingStrategy implements SessionHandlingStrategy {
	protected final Logger logger;
	protected final ExtendedSmppSessionConfiguration configuration;
	protected final SmppClientSupplier clientSupplier;
	protected final SmppSessionHandlerSupplier smppSessionHandlerSupplier;
	protected final RetryExecutor retry;
	protected SmppClient currentClient;
	protected SmppSession currentSession;

	public BaseSessionHandlingStrategy(Logger logger, ExtendedSmppSessionConfiguration configuration, SmppClientSupplier clientSupplier, SmppSessionHandlerSupplier smppSessionHandlerSupplier,
			RetryExecutor retry) {
		super();
		this.logger = logger;
		this.configuration = configuration;
		this.clientSupplier = clientSupplier;
		this.smppSessionHandlerSupplier = smppSessionHandlerSupplier;
		this.retry = retry;
	}

	/**
	 * Initializes a new session only if session doesn't exist (is
	 * {@code null}).
	 * 
	 * <p>
	 * A {@link SmppClient} instance must exist (either using
	 * {@link #initClient()} or by manually creating it). The same
	 * {@link SmppClient} may be used several times for different sessions.
	 * 
	 * <p>
	 * The creation of a session is done by calling
	 * {@link #connect(SmppClient)}.
	 * 
	 * @throws SmppException
	 *             when session couldn't be created
	 */
	protected synchronized void initSession() throws SmppException {
		if (currentSession == null) {
			logger.debug("Requesting a new SMPP session");
			currentSession = connect(currentClient);
			logger.debug("SMPP session bound");
		}
	}

	/**
	 * Connect the client to the SMSC using a
	 * {@link SmppClient#bind(com.cloudhopper.smpp.SmppSessionConfiguration, com.cloudhopper.smpp.SmppSessionHandler)}
	 * request.
	 * 
	 * <p>
	 * The configured retry strategy is used to send the bind request to the
	 * server i.e. several attempts may be done.
	 * 
	 * @param client
	 *            the client used to send to bind command
	 * @return the created session
	 * @throws SmppException
	 *             when the session couldn't be bound
	 */
	protected synchronized SmppSession connect(final SmppClient client) throws SmppException {
		try {
			return retry.execute(named("Connection to SMPP server", () -> client.bind(configuration, smppSessionHandlerSupplier.get())));
		} catch (RetryExecutionInterruptedException e) {
			Thread.currentThread().interrupt();
			throw new ConnectionFailedException("Failed to initialize SMPP session (interrupted)", e);
		} catch (MaximumAttemptsReachedException e) {
			throw new ConnectionFailedException("Failed to initialize SMPP session after maximum retries reached", e);
		} catch (RetryException e) {
			throw new ConnectionFailedException("Failed to initialize SMPP session", e);
		}
	}

	/**
	 * Create a {@link SmppClient} instance of not existing (is {@code null}).
	 */
	protected synchronized void initClient() {
		if (currentClient == null) {
			logger.debug("Requesting a new SmppClient instance");
			currentClient = clientSupplier.get();
		}
	}

	/**
	 * Send an unbind command to the server to properly close the session, close
	 * the session and cleanup everything related to the session. The current
	 * session is set to {@code null}.
	 */
	protected synchronized void destroySession() {
		if (currentSession != null) {
			logger.debug("Closing SMPP session");
			currentSession.unbind(configuration.getUnbindTimeout());
			currentSession.destroy();
			currentSession = null;
		}
	}

	/**
	 * Destroy (cleanup everything) the client. The client is set to
	 * {@code null}.
	 */
	protected synchronized void destroyClient() {
		if (currentClient != null) {
			logger.debug("Destroying SMPP client");
			currentClient.destroy();
			currentClient = null;
		}
	}
}