001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.smpp;
018
019 import java.io.IOException;
020
021 import org.apache.camel.Exchange;
022 import org.apache.camel.impl.DefaultProducer;
023 import org.apache.commons.logging.Log;
024 import org.apache.commons.logging.LogFactory;
025 import org.jsmpp.DefaultPDUReader;
026 import org.jsmpp.DefaultPDUSender;
027 import org.jsmpp.SynchronizedPDUSender;
028 import org.jsmpp.bean.Alphabet;
029 import org.jsmpp.bean.BindType;
030 import org.jsmpp.bean.ESMClass;
031 import org.jsmpp.bean.GeneralDataCoding;
032 import org.jsmpp.bean.MessageClass;
033 import org.jsmpp.bean.NumberingPlanIndicator;
034 import org.jsmpp.bean.RegisteredDelivery;
035 import org.jsmpp.bean.SubmitSm;
036 import org.jsmpp.bean.TypeOfNumber;
037 import org.jsmpp.extra.SessionState;
038 import org.jsmpp.session.BindParameter;
039 import org.jsmpp.session.SMPPSession;
040 import org.jsmpp.session.SessionStateListener;
041 import org.jsmpp.util.DefaultComposer;
042
043 /**
044 * An implementation of @{link Producer} which use the SMPP protocol
045 *
046 * @version $Revision: 991338 $
047 * @author muellerc
048 */
049 public class SmppProducer extends DefaultProducer {
050
051 private static final transient Log LOG = LogFactory.getLog(SmppProducer.class);
052
053 private SmppConfiguration configuration;
054 private SMPPSession session;
055 private SessionStateListener sessionStateListener;
056
057 public SmppProducer(SmppEndpoint endpoint, SmppConfiguration config) {
058 super(endpoint);
059 this.configuration = config;
060 this.sessionStateListener = new SessionStateListener() {
061 public void onStateChange(SessionState newState, SessionState oldState, Object source) {
062 if (newState.equals(SessionState.CLOSED)) {
063 LOG.warn("Loosing connection to: " + getEndpoint().getConnectionString() + " - trying to reconnect...");
064 closeSession(session);
065 reconnect(configuration.getInitialReconnectDelay());
066 }
067 }
068 };
069 }
070
071 @Override
072 protected void doStart() throws Exception {
073 LOG.debug("Connecting to: " + getEndpoint().getConnectionString() + "...");
074
075 super.doStart();
076 session = createSession();
077
078 LOG.info("Connected to: " + getEndpoint().getConnectionString());
079 }
080
081 private SMPPSession createSession() throws IOException {
082 SMPPSession session = createSMPPSession();
083 session.setEnquireLinkTimer(this.configuration.getEnquireLinkTimer());
084 session.setTransactionTimer(this.configuration.getTransactionTimer());
085 session.addSessionStateListener(sessionStateListener);
086 session.connectAndBind(
087 this.configuration.getHost(),
088 this.configuration.getPort(),
089 new BindParameter(
090 BindType.BIND_TX,
091 this.configuration.getSystemId(),
092 this.configuration.getPassword(),
093 this.configuration.getSystemType(),
094 TypeOfNumber.valueOf(configuration.getTypeOfNumber()),
095 NumberingPlanIndicator.valueOf(configuration.getNumberingPlanIndicator()),
096 ""));
097
098 return session;
099 }
100
101 /**
102 * Factory method to easily instantiate a mock SMPPSession
103 *
104 * @return the SMPPSession
105 */
106 SMPPSession createSMPPSession() {
107 if (configuration.getUsingSSL()) {
108 return new SMPPSession(new SynchronizedPDUSender(new DefaultPDUSender(new DefaultComposer())),
109 new DefaultPDUReader(), SmppSSLConnectionFactory.getInstance());
110 } else {
111 return new SMPPSession();
112 }
113 }
114
115 public void process(Exchange exchange) throws Exception {
116 if (LOG.isDebugEnabled()) {
117 LOG.debug("Sending a short message for exchange id '"
118 + exchange.getExchangeId() + "'...");
119 }
120
121 // only possible by trying to reconnect
122 if (this.session == null) {
123 throw new IOException("Lost connection to " + getEndpoint().getConnectionString() + " and yet not reconnected");
124 }
125
126 SubmitSm submitSm = getEndpoint().getBinding().createSubmitSm(exchange);
127 String messageId = session.submitShortMessage(
128 submitSm.getServiceType(),
129 TypeOfNumber.valueOf(submitSm.getSourceAddrTon()),
130 NumberingPlanIndicator.valueOf(submitSm.getSourceAddrNpi()),
131 submitSm.getSourceAddr(),
132 TypeOfNumber.valueOf(submitSm.getDestAddrTon()),
133 NumberingPlanIndicator.valueOf(submitSm.getDestAddrNpi()),
134 submitSm.getDestAddress(),
135 new ESMClass(),
136 submitSm.getProtocolId(),
137 submitSm.getPriorityFlag(),
138 submitSm.getScheduleDeliveryTime(),
139 submitSm.getValidityPeriod(),
140 new RegisteredDelivery(submitSm.getRegisteredDelivery()),
141 submitSm.getReplaceIfPresent(),
142 new GeneralDataCoding(
143 false,
144 false,
145 MessageClass.CLASS1,
146 Alphabet.valueOf(submitSm.getDataCoding())),
147 (byte) 0,
148 submitSm.getShortMessage());
149
150 if (LOG.isDebugEnabled()) {
151 LOG.debug("Sent a short message for exchange id '"
152 + exchange.getExchangeId() + "' and received message id '"
153 + messageId + "'");
154 }
155
156 if (exchange.getPattern().isOutCapable()) {
157 if (LOG.isDebugEnabled()) {
158 LOG.debug("Exchange is out capable, setting headers on out exchange...");
159 }
160 exchange.getOut().setHeader(SmppBinding.ID, messageId);
161 } else {
162 if (LOG.isDebugEnabled()) {
163 LOG.debug("Exchange is not out capable, setting headers on in exchange...");
164 }
165 exchange.getIn().setHeader(SmppBinding.ID, messageId);
166 }
167 }
168
169 @Override
170 protected void doStop() throws Exception {
171 LOG.debug("Disconnecting from: " + getEndpoint().getConnectionString() + "...");
172
173 super.doStop();
174 closeSession(session);
175
176 LOG.info("Disconnected from: " + getEndpoint().getConnectionString());
177 }
178
179 private void closeSession(SMPPSession session) {
180 if (session != null) {
181 session.removeSessionStateListener(this.sessionStateListener);
182 session.close();
183 session = null;
184 }
185 }
186
187 private void reconnect(final long initialReconnectDelay) {
188 new Thread() {
189 @Override
190 public void run() {
191 LOG.info("Schedule reconnect after " + initialReconnectDelay + " millis");
192 try {
193 Thread.sleep(initialReconnectDelay);
194 } catch (InterruptedException e) {
195 }
196
197 int attempt = 0;
198 while (!(isStopping() || isStopped()) && (session == null || session.getSessionState().equals(SessionState.CLOSED))) {
199 try {
200 LOG.info("Trying to reconnect to " + getEndpoint().getConnectionString() + " - attempt #" + (++attempt) + "...");
201 session = createSession();
202 } catch (IOException e) {
203 LOG.info("Failed to reconnect to " + getEndpoint().getConnectionString());
204 closeSession(session);
205 try {
206 Thread.sleep(configuration.getReconnectDelay());
207 } catch (InterruptedException ee) {
208 }
209 }
210 }
211 LOG.info("Reconnected to " + getEndpoint().getConnectionString());
212 }
213 }.start();
214 }
215
216 @Override
217 public SmppEndpoint getEndpoint() {
218 return (SmppEndpoint) super.getEndpoint();
219 }
220
221 /**
222 * Returns the smppConfiguration for this producer
223 *
224 * @return the configuration
225 */
226 public SmppConfiguration getConfiguration() {
227 return configuration;
228 }
229
230 @Override
231 public String toString() {
232 return "SmppProducer[" + getEndpoint().getConnectionString() + "]";
233 }
234 }