// SMSLib for Java v3
// A Java API library for sending and receiving SMS via a GSM modem
// or other supported gateways.
// Web Site: http://www.smslib.org
//
// Copyright (C) 2002-2012, Thanasis Delenikas, Athens/GREECE.
// SMSLib is distributed under the terms of the Apache License version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.smslib.smpp.jsmpp;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import org.jsmpp.InvalidResponseException;
import org.jsmpp.PDUException;
import org.jsmpp.bean.AlertNotification;
import org.jsmpp.bean.Alphabet;
import org.jsmpp.bean.BindType;
import org.jsmpp.bean.DataSm;
import org.jsmpp.bean.DeliverSm;
import org.jsmpp.bean.DeliveryReceipt;
import org.jsmpp.bean.ESMClass;
import org.jsmpp.bean.GeneralDataCoding;
import org.jsmpp.bean.MessageClass;
import org.jsmpp.bean.MessageType;
import org.jsmpp.bean.NumberingPlanIndicator;
import org.jsmpp.bean.RegisteredDelivery;
import org.jsmpp.bean.SMSCDeliveryReceipt;
import org.jsmpp.bean.TypeOfNumber;
import org.jsmpp.extra.NegativeResponseException;
import org.jsmpp.extra.ProcessRequestException;
import org.jsmpp.extra.ResponseTimeoutException;
import org.jsmpp.extra.SessionState;
import org.jsmpp.session.BindParameter;
import org.jsmpp.session.DataSmResult;
import org.jsmpp.session.MessageReceiverListener;
import org.jsmpp.session.SMPPSession;
import org.jsmpp.session.Session;
import org.jsmpp.session.SessionStateListener;
import org.jsmpp.util.InvalidDeliveryReceiptException;
import org.smslib.AGateway;
import org.smslib.GatewayException;
import org.smslib.InboundMessage;
import org.smslib.OutboundMessage;
import org.smslib.Service;
import org.smslib.StatusReportMessage;
import org.smslib.TimeoutException;
import org.smslib.Message.MessageEncodings;
import org.smslib.Message.MessageTypes;
import org.smslib.OutboundMessage.FailureCauses;
import org.smslib.OutboundMessage.MessageStatuses;
import org.smslib.StatusReportMessage.DeliveryStatuses;
import org.smslib.helper.Logger;
import org.smslib.notify.InboundMessageNotification;
import org.smslib.smpp.AbstractSMPPGateway;
import org.smslib.smpp.BindAttributes;
/**
* A gateway that supports SMPP through JSMPP (http://code.google.com/p/jsmpp/).
*
* @author Bassam Al-Sarori
*/
public class JSMPPGateway extends AbstractSMPPGateway {
private SMPPSession session = null;
private MessageReceiver messageReceiver=new MessageReceiver();
private SessionStateListener stateListener=new JSMPPSessionStateListener();
private BindType bindType;
private TypeOfNumber bindTypeOfNumber;
private NumberingPlanIndicator bindNumberingPlanIndicator;
/**
* @param id
* @param host
* @param port
* @param bindAttributes
*/
public JSMPPGateway(String id, String host, int port,
BindAttributes bindAttributes) {
super(id, host, port, bindAttributes);
setAttributes(AGateway.GatewayAttributes.SEND | AGateway.GatewayAttributes.CUSTOMFROM | AGateway.GatewayAttributes.BIGMESSAGES | AGateway.GatewayAttributes.FLASHSMS | AGateway.GatewayAttributes.RECEIVE);
init();
}
private void init(){
switch (bindAttributes.getBindType()){
case RECEIVER:
bindType=BindType.BIND_RX;
setInbound(true);
setOutbound(false);
break;
case TRANSMITTER:
bindType=BindType.BIND_TX;
setInbound(false);
setOutbound(true);
break;
case TRANSCEIVER:
bindType=BindType.BIND_TRX;
setInbound(true);
setOutbound(true);
break;
default:
IllegalArgumentException illegalArgumentException=new IllegalArgumentException("Unknown BindType "+bindAttributes.getBindType());
Logger.getInstance().logError(illegalArgumentException.getMessage(), illegalArgumentException, getGatewayId());
throw illegalArgumentException;
}
bindTypeOfNumber=TypeOfNumber.valueOf(bindAttributes.getBindAddress().getTypeOfNumber().value());
bindNumberingPlanIndicator=NumberingPlanIndicator.valueOf(bindAttributes.getBindAddress().getNumberingPlanIndicator().value());
initSession();
}
private void initSession(){
session = new SMPPSession();
session.addSessionStateListener(stateListener);
session.setMessageReceiverListener(messageReceiver);
}
@Override
public void startGateway() throws TimeoutException, GatewayException,
IOException, InterruptedException {
if(!session.getSessionState().isBound()){
if(enquireLink>0){
session.setEnquireLinkTimer(enquireLink);
}
session.connectAndBind(host, port, new BindParameter(bindType, bindAttributes.getSystemId(), bindAttributes.getPassword(), bindAttributes.getSystemType(), bindTypeOfNumber, bindNumberingPlanIndicator, null));
}else{
Logger.getInstance().logWarn("SMPP session already bound.", null, getGatewayId());
// throw new GatewayException("Session already bound");
}
}
@Override
public void stopGateway() throws TimeoutException, GatewayException,
IOException, InterruptedException {
if(session.getSessionState().isBound()){
session.removeSessionStateListener(stateListener);
session.unbindAndClose();
//super.stopGateway();
}else{
Logger.getInstance().logWarn("SMPP session not bound.", null, getGatewayId());
//throw new GatewayException("Session not bound");
}
super.stopGateway();
}
class MessageReceiver implements MessageReceiverListener{
public void onAcceptDeliverSm(DeliverSm deliverSm)
throws ProcessRequestException {
if (MessageType.SMSC_DEL_RECEIPT.containedIn(deliverSm.getEsmClass())) {
try {
DeliveryReceipt delReceipt = deliverSm.getShortMessageAsDeliveryReceipt();
StatusReportMessage statusReportMessage=new StatusReportMessage(delReceipt.getId(),deliverSm.getDestAddress(), deliverSm.getSourceAddr(), delReceipt.getText(), delReceipt.getSubmitDate(), delReceipt.getDoneDate());
switch(delReceipt.getFinalStatus()){
case DELIVRD:
statusReportMessage.setStatus(DeliveryStatuses.DELIVERED);
break;
case REJECTD:
case EXPIRED:
case UNDELIV:
statusReportMessage.setStatus(DeliveryStatuses.ABORTED);
break;
default:
statusReportMessage.setStatus(DeliveryStatuses.UNKNOWN);
}
statusReportMessage.setGatewayId(getGatewayId());
Service.getInstance().getNotifyQueueManager().getNotifyQueue().add(new InboundMessageNotification(getMyself(), MessageTypes.STATUSREPORT, statusReportMessage));
} catch (InvalidDeliveryReceiptException e) {
Logger.getInstance().logError("Failed getting delivery receipt.", e, getGatewayId());
}
} else {
InboundMessage msg = new InboundMessage(new java.util.Date(), deliverSm.getSourceAddr(), new String(deliverSm.getShortMessage()), 0, null);
msg.setGatewayId(JSMPPGateway.this.getGatewayId());
if(Alphabet.ALPHA_DEFAULT.value()==deliverSm.getDataCoding()){
msg.setEncoding(MessageEncodings.ENC7BIT);
}else if(Alphabet.ALPHA_8_BIT.value()==deliverSm.getDataCoding()){
msg.setEncoding(MessageEncodings.ENC8BIT);
}else if(Alphabet.ALPHA_UCS2.value()==deliverSm.getDataCoding()){
msg.setEncoding(MessageEncodings.ENCUCS2);
}else{
msg.setEncoding(MessageEncodings.ENCCUSTOM);
}
incInboundMessageCount();
Service.getInstance().getNotifyQueueManager().getNotifyQueue().add(new InboundMessageNotification(getMyself(), MessageTypes.INBOUND, msg));
}
}
public DataSmResult onAcceptDataSm(DataSm dataSm, Session source)
throws ProcessRequestException {
// ignored
return null;
}
public void onAcceptAlertNotification(
AlertNotification alertNotification) {
// ignored
}
}
class JSMPPSessionStateListener implements SessionStateListener {
public void onStateChange(SessionState newState, SessionState oldState,
Object source) {
if(newState.isBound()){
if(!getStatus().equals(GatewayStatuses.STARTED)){
try {
JSMPPGateway.super.startGateway();
} catch (TimeoutException e) {
Logger.getInstance().logError("Failed starting Gateway.", e, getGatewayId());
} catch (GatewayException e) {
Logger.getInstance().logError("Failed starting Gateway.", e, getGatewayId());
} catch (IOException e) {
Logger.getInstance().logError("Failed starting Gateway.", e, getGatewayId());
} catch (InterruptedException e) {
Logger.getInstance().logError("Failed starting Gateway.", e, getGatewayId());
}
}
}else if(newState.equals(SessionState.CLOSED)){
if(getStatus().equals(GatewayStatuses.STARTED)){
JSMPPGateway.super.setStatus(GatewayStatuses.RESTART);
initSession();
}
}
//System.out.println("State Changed: from "+oldState+" To "+newState);
}
}
@Override
public boolean sendMessage(OutboundMessage msg) throws TimeoutException,
GatewayException, IOException, InterruptedException {
Alphabet encoding=Alphabet.ALPHA_DEFAULT;
switch (msg.getEncoding()){
case ENC8BIT:
encoding=Alphabet.ALPHA_8_BIT;
break;
case ENCUCS2:
encoding=Alphabet.ALPHA_UCS2;
break;
case ENCCUSTOM:
encoding=Alphabet.ALPHA_RESERVED;
break;
}
GeneralDataCoding dataCoding;
switch(msg.getDCSMessageClass()){
case MSGCLASS_FLASH:
dataCoding=new GeneralDataCoding(false, true, MessageClass.CLASS0, encoding);
break;
case MSGCLASS_ME:
dataCoding=new GeneralDataCoding(false, true, MessageClass.CLASS1, encoding);
break;
case MSGCLASS_SIM:
dataCoding=new GeneralDataCoding(false, true, MessageClass.CLASS2, encoding);
break;
case MSGCLASS_TE:
dataCoding=new GeneralDataCoding(false, true, MessageClass.CLASS3, encoding);
break;
default:
dataCoding=new GeneralDataCoding();
dataCoding.setAlphabet(encoding);
}
try {
final RegisteredDelivery registeredDelivery = new RegisteredDelivery();
registeredDelivery.setSMSCDeliveryReceipt((msg.getStatusReport())?SMSCDeliveryReceipt.SUCCESS_FAILURE:SMSCDeliveryReceipt.DEFAULT);
String msgId=session.submitShortMessage(bindAttributes.getSystemType(),
TypeOfNumber.valueOf(sourceAddress.getTypeOfNumber().value()),
NumberingPlanIndicator.valueOf(sourceAddress.getNumberingPlanIndicator().value()),
(msg.getFrom()!=null)?msg.getFrom():getFrom(),
TypeOfNumber.valueOf(destinationAddress.getTypeOfNumber().value()),
NumberingPlanIndicator.valueOf(destinationAddress.getNumberingPlanIndicator().value()),
msg.getRecipient(),
new ESMClass(),
(byte)0,
(byte)msg.getPriority(),
null,
formatTimeFromHours(msg.getValidityPeriod()),
registeredDelivery,
(byte)0,
dataCoding,
(byte)0,
msg.getText().getBytes());
msg.setRefNo(msgId);
msg.setDispatchDate(new Date());
msg.setGatewayId(getGatewayId());
msg.setMessageStatus(MessageStatuses.SENT);
incOutboundMessageCount();
}catch (PDUException e) {
msg.setGatewayId(getGatewayId());
msg.setMessageStatus(MessageStatuses.FAILED);
msg.setFailureCause(FailureCauses.BAD_FORMAT);
Logger.getInstance().logError("Message Format not accepted.", e, getGatewayId());
return false;
} catch (ResponseTimeoutException e) {
Logger.getInstance().logError("Message could not be sent.", e, getGatewayId());
throw new TimeoutException(e.getMessage());
} catch (InvalidResponseException e) {
Logger.getInstance().logError("Message could not be sent.", e, getGatewayId());
throw new IOException("InvalidResponseException: ", e);
} catch (NegativeResponseException e) {
Logger.getInstance().logError("Message could not be sent.", e, getGatewayId());
throw new IOException("NegativeResponseException: ", e);
}
return true;
}
private String formatTimeFromHours(int timeInHours){
if(timeInHours<0){
return null;
}
Calendar cDate=Calendar.getInstance();
cDate.clear();
cDate.set(Calendar.YEAR, 0);
cDate.add(Calendar.HOUR, timeInHours);
int years=cDate.get(Calendar.YEAR)-cDate.getMinimum(Calendar.YEAR);
int months=cDate.get(Calendar.MONTH);
int days=cDate.get(Calendar.DAY_OF_MONTH)-1;
int hours=cDate.get(Calendar.HOUR_OF_DAY);
String yearsString=(years<10)?"0"+years:years+"";
String monthsString=(months<10)?"0"+months:months+"";
String daysString=(days<10)?"0"+days:days+"";
String hoursString=(hours<10)?"0"+hours:hours+"";
return yearsString+monthsString+daysString+hoursString+"0000000R";
}
@Override
public void setEnquireLink(int enquireLink) {
super.setEnquireLink(enquireLink);
if(session!=null){
session.setEnquireLinkTimer(enquireLink);
}
}
}