MayReuseSessionStrategy.java
package fr.sii.ogham.sms.sender.impl.cloudhopper.session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cloudhopper.smpp.SmppClient;
import com.cloudhopper.smpp.SmppSession;
import com.cloudhopper.smpp.pdu.EnquireLink;
import com.cloudhopper.smpp.type.RecoverablePduException;
import com.cloudhopper.smpp.type.SmppChannelException;
import com.cloudhopper.smpp.type.SmppTimeoutException;
import com.cloudhopper.smpp.type.UnrecoverablePduException;
import fr.sii.ogham.core.exception.MessageException;
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.message.Sms;
import fr.sii.ogham.sms.sender.impl.cloudhopper.ExtendedSmppSessionConfiguration;
import fr.sii.ogham.sms.sender.impl.cloudhopper.exception.SmppException;
/**
* Strategy that attempts to reuse the previous session if possible.
*
* <p>
* When sending the first message, a new session is created. Later, when sending
* the next message, if the session is still alive, this session is reused. As
* the connection is not actively maintained, the session may be killed by the
* server. Therefore to check if the session is still alive, an
* {@link EnquireLink} request is sent. If a response is received from the
* server, then the session is still alive and the message can be sent using the
* same session. If a failure response or no response is received after some
* time from the server, then a new session must be created.
*
* <p>
* To check if the session is still alive, the {@link EnquireLink} request is
* sent just before sending the real message. In order to prevent sending an
* {@link EnquireLink} request before <strong>every</strong> message, the date
* of the last sent message or {@link EnquireLink} is kept. This date is
* compared to a delay to ensure that no {@link EnquireLink} is sent during this
* delay.
*
* @author Aurélien Baudet
*
*/
public class MayReuseSessionStrategy extends BaseSessionHandlingStrategy implements ErrorHandler {
private static final Logger LOG = LoggerFactory.getLogger(MayReuseSessionStrategy.class);
private final ErrorAnalyzer errorAnalyzer;
private long lastSentOrSession;
public MayReuseSessionStrategy(ExtendedSmppSessionConfiguration configuration, SmppClientSupplier clientSupplier, SmppSessionHandlerSupplier smppSessionHandlerSupplier, RetryExecutor retry,
ErrorAnalyzer errorAnalyzer) {
super(LOG, configuration, clientSupplier, smppSessionHandlerSupplier, retry);
this.errorAnalyzer = errorAnalyzer;
}
@Override
public SmppSession getSession() throws SmppException {
initClient();
initSession();
if (!isSessionStillAlive()) {
LOG.debug("Current session seems to be broken. Trying to reconnect...");
reconnect();
}
return currentSession;
}
@Override
public void messageSent(Sms sms) throws MessageException {
updateLastSentOrSession();
}
@Override
public void messageNotSent(Sms sms, SmppException e) throws MessageException {
// If message couldn't be sent, re-throw the exception.
//
// The cause may be a broken connection because the delay to wait before
// sending a new EnquireLink is too long compared to the server
// configuration. But in this case, the developer has to adjust the
// settings accordingly to the server.
//
// If a new connection is required, clean the current session now
// therefore a new session will be created next time.
if (errorAnalyzer.requiresNewConnection(e)) {
clean();
}
throw new MessageException("Failed to send SMS", sms, e);
}
@Override
public void handleFailure(Throwable e) {
// If a new connection is required, clean the current session now
// therefore a new session will be created next time.
if (errorAnalyzer.requiresNewConnection(e)) {
clean();
}
}
@Override
public void messageProcessed(Sms sms) {
// nothing to do
}
@Override
public void clean() {
destroySession();
destroyClient();
}
@Override
protected synchronized SmppSession connect(SmppClient client) throws SmppException {
SmppSession session = super.connect(client);
updateLastSentOrSession();
return session;
}
private boolean isSessionStillAlive() {
long elapsedTime = now() - lastSentOrSession;
boolean skipEnquireLink = elapsedTime < configuration.getReuseSession().getLastInteractionExpirationDelay();
LOG.trace("Skip EnquireLink? {} {} => {}", elapsedTime, configuration.getReuseSession().getLastInteractionExpirationDelay(), skipEnquireLink);
if (skipEnquireLink) {
return true;
}
try {
LOG.trace("Sending EnquireLink to check if session is still alive...");
currentSession.enquireLink(new EnquireLink(), configuration.getReuseSession().getEnquireLinkTimeout());
LOG.trace("Session is still alive");
updateLastSentOrSession();
return true;
} catch (RecoverablePduException | UnrecoverablePduException | SmppTimeoutException | SmppChannelException e) {
LOG.trace("Failure while sending EnquireLink", e);
} catch (InterruptedException e) {
LOG.trace("Failure while sending EnquireLink (interrupted)", e);
Thread.currentThread().interrupt();
}
return false;
}
private void reconnect() throws SmppException {
destroySession();
destroyClient();
initClient();
initSession();
}
private void updateLastSentOrSession() {
lastSentOrSession = now();
LOG.trace("lastSentOrSession updated: {}", lastSentOrSession);
}
private static long now() {
return System.currentTimeMillis();
}
}