All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mobicents.smsc.smpp.SmppClientOpsThread Maven / Gradle / Ivy

/*
 * TeleStax, Open Source Cloud Communications  
 * Copyright 2012, Telestax Inc and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.mobicents.smsc.smpp;

import java.util.Iterator;

import javolution.util.FastList;

import org.apache.log4j.Logger;

import com.cloudhopper.smpp.PduAsyncResponse;
import com.cloudhopper.smpp.SmppSession;
import com.cloudhopper.smpp.SmppSessionConfiguration;
import com.cloudhopper.smpp.SmppSessionHandler;
import com.cloudhopper.smpp.impl.DefaultSmppClient;
import com.cloudhopper.smpp.impl.DefaultSmppSession;
import com.cloudhopper.smpp.pdu.EnquireLink;
import com.cloudhopper.smpp.pdu.PduRequest;
import com.cloudhopper.smpp.pdu.PduResponse;
import com.cloudhopper.smpp.ssl.SslConfiguration;
import com.cloudhopper.smpp.type.Address;
import com.cloudhopper.smpp.type.RecoverablePduException;
import com.cloudhopper.smpp.type.UnrecoverablePduException;

/**
 * @author Amit Bhayani
 * 
 */
public class SmppClientOpsThread implements Runnable {

	private static final Logger logger = Logger.getLogger(SmppClientOpsThread.class);

	private static final long SCHEDULE_CONNECT_DELAY = 1000 * 30; // 30 sec

	protected volatile boolean started = true;

	private FastList pendingChanges = new FastList();

	private Object waitObject = new Object();

	private final DefaultSmppClient clientBootstrap;
	private final SmppSessionHandlerInterface smppSessionHandlerInterface;

	/**
	 * 
	 */
	public SmppClientOpsThread(DefaultSmppClient clientBootstrap,
			SmppSessionHandlerInterface smppSessionHandlerInterface) {
		this.clientBootstrap = clientBootstrap;
		this.smppSessionHandlerInterface = smppSessionHandlerInterface;
	}

	/**
	 * @param started
	 *            the started to set
	 */
	protected void setStarted(boolean started) {
		this.started = started;

		synchronized (this.waitObject) {
			this.waitObject.notify();
		}
	}

	protected void scheduleConnect(Esme esme) {
		synchronized (this.pendingChanges) {
			this.pendingChanges.add(new ChangeRequest(esme, ChangeRequest.CONNECT, System.currentTimeMillis()
					+ SCHEDULE_CONNECT_DELAY));
		}

		synchronized (this.waitObject) {
			this.waitObject.notify();
		}

	}

	protected void scheduleEnquireLink(Esme esme) {
		synchronized (this.pendingChanges) {
			this.pendingChanges.add(new ChangeRequest(esme, ChangeRequest.ENQUIRE_LINK, System.currentTimeMillis()
					+ esme.getEnquireLinkDelay()));
		}

		synchronized (this.waitObject) {
			this.waitObject.notify();
		}
	}

	@Override
	public void run() {
		if (logger.isInfoEnabled()) {
			logger.info("SmppClientOpsThread started.");
		}

		while (this.started) {
			FastList pendingList = new FastList<>();

			try {
				synchronized (this.pendingChanges) {
					Iterator changes = pendingChanges.iterator();

					while (changes.hasNext()) {
						ChangeRequest change = changes.next();
						switch (change.getType()) {
						case ChangeRequest.CONNECT:
							if (!change.getEsme().isStarted()) {
								pendingChanges.remove(change);
							} else {
								if (change.getExecutionTime() <= System.currentTimeMillis()) {
									pendingChanges.remove(change);
									initiateConnection(change.getEsme());
								}
							}
							break;
						case ChangeRequest.ENQUIRE_LINK:
							if (!change.getEsme().isStarted()) {
								pendingChanges.remove(change);
							} else {
								if (change.getEsme().getEnquireClientEnabled()) {

									if (change.getExecutionTime() <= System.currentTimeMillis()) {
										pendingList.add(change.getEsme());
										pendingChanges.remove(change);
									}
								}
							}
							break;
						}
					}
				}

				// Sending Enquire messages
				Iterator pendingchanges = pendingList.iterator();
				while (pendingchanges.hasNext()) {
					Esme change = pendingchanges.next();
					this.enquireLink(change);
				}

				synchronized (this.waitObject) {
					this.waitObject.wait(5000);
				}

			} catch (InterruptedException e) {
				logger.error("Error while looping SmppClientOpsThread thread", e);
			}
		}// while

		if (logger.isInfoEnabled()) {
			logger.info("SmppClientOpsThread for stopped.");
		}
	}

	private void enquireLink(Esme esme) {
		SmppSession smppSession = esme.getSmppSession();

		if (!esme.isStarted()) {
			return;
		}

		if (smppSession != null && smppSession.isBound()) {
			try {
				smppSession.enquireLink(new EnquireLink(), 10000);

				// all ok lets schedule another ENQUIRE_LINK
				this.scheduleEnquireLink(esme);
				return;

			} catch (RecoverablePduException e) {
				logger.warn(String.format("RecoverablePduException while sending the ENQURE_LINK for ESME SystemId=%s",
						esme.getSystemId()), e);

				// Recoverabel exception is ok
				// all ok lets schedule another ENQUIRE_LINK
				this.scheduleEnquireLink(esme);
				return;

			} catch (Exception e) {

				logger.error(
						String.format("Exception while trying to send ENQUIRE_LINK for ESME SystemId=%s",
								esme.getSystemId()), e);
				// For all other exceptions lets close session and re-try
				// connect
				try {
					smppSession.close();
				} catch (Exception ex) {
					logger.error(String.format("Failed to close smpp client session for %s.",
							smppSession.getConfiguration().getName()));
				}
				this.scheduleConnect(esme);
			}

		} else {
			// This should never happen
			logger.warn(String.format("Sending ENQURE_LINK fialed for ESME SystemId=%s as SmppSession is =%s !",
					esme.getSystemId(), (smppSession == null ? null : smppSession.getStateName())));

			if (smppSession != null) {
				try {
					smppSession.close();
				} catch (Exception e) {
					logger.error(String.format("Failed to close smpp client session for %s.",
							smppSession.getConfiguration().getName()));
				}
			}
			this.scheduleConnect(esme);
		}
	}

	private void initiateConnection(Esme esme) {
		// If Esme is stopped, don't try to initiate connect
		if (!esme.isStarted()) {
			return;
		}

		SmppSession smppSession = esme.getSmppSession();
		if ((smppSession != null && smppSession.isBound()) || (smppSession != null && smppSession.isBinding())) {
			// If process has already begun lets not do it again
			return;
		}

		SmppSession session0 = null;
		try {

			SmppSessionConfiguration config0 = new SmppSessionConfiguration();
			config0.setWindowSize(esme.getWindowSize());
			config0.setName(esme.getSystemId());
			config0.setType(esme.getSmppBindType());
			config0.setBindTimeout(esme.getClientBindTimeout());
			config0.setHost(esme.getHost());
			config0.setPort(esme.getPort());
			config0.setConnectTimeout(esme.getConnectTimeout());
			config0.setSystemId(esme.getSystemId());
			config0.setPassword(esme.getPassword());
			config0.setSystemType(esme.getSystemType());
			config0.getLoggingOptions().setLogBytes(true);
			// to enable monitoring (request expiration)
			config0.setRequestExpiryTimeout(esme.getRequestExpiryTimeout());
			config0.setWindowMonitorInterval(esme.getWindowMonitorInterval());
			config0.setCountersEnabled(esme.isCountersEnabled());
			
			int addressTon = esme.getEsmeTon();
			int addressNpi = esme.getEsmeNpi();
			String addressRange = esme.getEsmeAddressRange();
			
			Address addressRangeObj = new Address();
			if(addressTon!=-1){
				addressRangeObj.setTon((byte)addressTon);
			}
			
			if(addressNpi != -1){
				addressRangeObj.setNpi((byte)addressNpi);
			}
			
			if(addressRange != null){
				addressRangeObj.setAddress(addressRange);
			}
			
			config0.setAddressRange(addressRangeObj);

			SmppSessionHandler sessionHandler = new ClientSmppSessionHandler(esme,
					this.smppSessionHandlerInterface.createNewSmppSessionHandler(esme));
			
			// SSL settings
			if (esme.isUseSsl()) {
				logger.info(String.format("%s ESME will use SSL Configuration", esme.getName()));
				SslConfiguration sslConfiguration = esme.getWrappedSslConfig();

				config0.setUseSsl(true);
				config0.setSslConfiguration(sslConfiguration);
			}

			session0 = clientBootstrap.bind(config0, sessionHandler);

			// Set in ESME
			esme.setSmppSession((DefaultSmppSession) session0);

			// Finally set Enquire Link schedule
			this.scheduleEnquireLink(esme);
		} catch (Exception e) {
			logger.error(
					String.format("Exception when trying to bind client SMPP connection for ESME systemId=%s",
							esme.getSystemId()), e);
			if (session0 != null) {
				session0.close();
			}
			this.scheduleConnect(esme);
		}
	}

	protected class ClientSmppSessionHandler implements SmppSessionHandler {

		private final Esme esme;
		private final SmppSessionHandler wrappedSmppSessionHandler;

		/**
		 * @param esme
		 */
		public ClientSmppSessionHandler(Esme esme, SmppSessionHandler wrappedSmppSessionHandler) {
			super();
			this.esme = esme;
			this.wrappedSmppSessionHandler = wrappedSmppSessionHandler;
		}

		@Override
		public String lookupResultMessage(int arg0) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public String lookupTlvTagName(short arg0) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public void fireChannelUnexpectedlyClosed() {
			this.wrappedSmppSessionHandler.fireChannelUnexpectedlyClosed();

			this.esme.getSmppSession().close();

			// Schedule the connection again
			scheduleConnect(this.esme);
		}

		@Override
		public void fireExpectedPduResponseReceived(PduAsyncResponse pduAsyncResponse) {
			this.wrappedSmppSessionHandler.fireExpectedPduResponseReceived(pduAsyncResponse);
		}

		@Override
		public void firePduRequestExpired(PduRequest pduRequest) {
			this.wrappedSmppSessionHandler.firePduRequestExpired(pduRequest);
		}

		@Override
		public PduResponse firePduRequestReceived(PduRequest pduRequest) {
			return this.wrappedSmppSessionHandler.firePduRequestReceived(pduRequest);
		}

		@Override
		public void fireRecoverablePduException(RecoverablePduException e) {
			this.wrappedSmppSessionHandler.fireRecoverablePduException(e);
		}

		@Override
		public void fireUnexpectedPduResponseReceived(PduResponse pduResponse) {
			this.wrappedSmppSessionHandler.fireUnexpectedPduResponseReceived(pduResponse);
		}

		@Override
		public void fireUnknownThrowable(Throwable e) {
			this.wrappedSmppSessionHandler.fireUnknownThrowable(e);
			// TODO is this ok?

			this.esme.getSmppSession().close();

			// Schedule the connection again
			scheduleConnect(this.esme);

		}

		@Override
		public void fireUnrecoverablePduException(UnrecoverablePduException e) {
			// TODO shall we call wrapped?
			this.wrappedSmppSessionHandler.fireUnrecoverablePduException(e);

			this.esme.getSmppSession().close();

			// Schedule the connection again
			scheduleConnect(this.esme);
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy