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 import java.util.concurrent.locks.ReentrantLock;
021
022 import org.apache.camel.Exchange;
023 import org.apache.camel.Processor;
024 import org.apache.camel.impl.DefaultConsumer;
025 import org.jsmpp.DefaultPDUReader;
026 import org.jsmpp.DefaultPDUSender;
027 import org.jsmpp.SynchronizedPDUSender;
028 import org.jsmpp.bean.AlertNotification;
029 import org.jsmpp.bean.BindType;
030 import org.jsmpp.bean.DataSm;
031 import org.jsmpp.bean.DeliverSm;
032 import org.jsmpp.bean.NumberingPlanIndicator;
033 import org.jsmpp.bean.TypeOfNumber;
034 import org.jsmpp.extra.ProcessRequestException;
035 import org.jsmpp.extra.SessionState;
036 import org.jsmpp.session.BindParameter;
037 import org.jsmpp.session.DataSmResult;
038 import org.jsmpp.session.MessageReceiverListener;
039 import org.jsmpp.session.SMPPSession;
040 import org.jsmpp.session.Session;
041 import org.jsmpp.session.SessionStateListener;
042 import org.jsmpp.util.DefaultComposer;
043 import org.jsmpp.util.MessageIDGenerator;
044 import org.jsmpp.util.MessageId;
045 import org.jsmpp.util.RandomMessageIDGenerator;
046 import org.slf4j.Logger;
047 import org.slf4j.LoggerFactory;
048
049 /**
050 * An implementation of @{link Consumer} which use the SMPP protocol
051 *
052 * @version
053 */
054 public class SmppConsumer extends DefaultConsumer {
055
056 private static final transient Logger LOG = LoggerFactory.getLogger(SmppConsumer.class);
057
058 private SmppConfiguration configuration;
059 private SMPPSession session;
060 private MessageReceiverListener messageReceiverListener;
061 private SessionStateListener sessionStateListener;
062 private final ReentrantLock reconnectLock = new ReentrantLock();
063
064 /**
065 * The constructor which gets a smpp endpoint, a smpp configuration and a
066 * processor
067 */
068 public SmppConsumer(SmppEndpoint endpoint, SmppConfiguration config, Processor processor) {
069 super(endpoint, processor);
070
071 this.configuration = config;
072 this.sessionStateListener = new SessionStateListener() {
073 public void onStateChange(SessionState newState, SessionState oldState, Object source) {
074 if (newState.equals(SessionState.CLOSED)) {
075 LOG.warn("Lost connection to: " + getEndpoint().getConnectionString()
076 + " - trying to reconnect...");
077 closeSession();
078 reconnect(configuration.getInitialReconnectDelay());
079 }
080 }
081 };
082 this.messageReceiverListener = new MessageReceiverListener() {
083 private final MessageIDGenerator messageIDGenerator = new RandomMessageIDGenerator();
084
085 public void onAcceptAlertNotification(AlertNotification alertNotification) {
086 if (LOG.isDebugEnabled()) {
087 LOG.debug("Received an alertNotification " + alertNotification);
088 }
089
090 try {
091 Exchange exchange = getEndpoint().createOnAcceptAlertNotificationExchange(
092 alertNotification);
093
094 LOG.trace("Processing the new smpp exchange...");
095 getProcessor().process(exchange);
096 LOG.trace("Processed the new smpp exchange");
097 } catch (Exception e) {
098 getExceptionHandler().handleException(e);
099 }
100 }
101
102 public void onAcceptDeliverSm(DeliverSm deliverSm) {
103 if (LOG.isDebugEnabled()) {
104 LOG.debug("Received a deliverSm " + deliverSm);
105 }
106
107 try {
108 Exchange exchange = getEndpoint().createOnAcceptDeliverSmExchange(deliverSm);
109
110 LOG.trace("processing the new smpp exchange...");
111 getProcessor().process(exchange);
112 LOG.trace("processed the new smpp exchange");
113 } catch (Exception e) {
114 getExceptionHandler().handleException(e);
115 }
116 }
117
118 public DataSmResult onAcceptDataSm(DataSm dataSm, Session session)
119 throws ProcessRequestException {
120 if (LOG.isDebugEnabled()) {
121 LOG.debug("Received a dataSm " + dataSm);
122 }
123
124 MessageId newMessageId = messageIDGenerator.newMessageId();
125
126 try {
127 Exchange exchange = getEndpoint().createOnAcceptDataSm(dataSm,
128 newMessageId.getValue());
129
130 LOG.trace("processing the new smpp exchange...");
131 getProcessor().process(exchange);
132 LOG.trace("processed the new smpp exchange");
133 } catch (Exception e) {
134 getExceptionHandler().handleException(e);
135 throw new ProcessRequestException(e.getMessage(), 255, e);
136 }
137
138 return new DataSmResult(newMessageId, dataSm.getOptionalParametes());
139 }
140 };
141 }
142
143 @Override
144 protected void doStart() throws Exception {
145 LOG.debug("Connecting to: " + getEndpoint().getConnectionString() + "...");
146
147 super.doStart();
148 session = createSession();
149
150 LOG.info("Connected to: " + getEndpoint().getConnectionString());
151 }
152
153 private SMPPSession createSession() throws IOException {
154 SMPPSession session = createSMPPSession();
155 session.setEnquireLinkTimer(configuration.getEnquireLinkTimer());
156 session.setTransactionTimer(configuration.getTransactionTimer());
157 session.addSessionStateListener(sessionStateListener);
158 session.setMessageReceiverListener(messageReceiverListener);
159 session.connectAndBind(this.configuration.getHost(), this.configuration.getPort(),
160 new BindParameter(BindType.BIND_RX, this.configuration.getSystemId(),
161 this.configuration.getPassword(), this.configuration.getSystemType(),
162 TypeOfNumber.UNKNOWN, NumberingPlanIndicator.UNKNOWN, ""));
163
164 return session;
165 }
166
167 /**
168 * Factory method to easily instantiate a mock SMPPSession
169 *
170 * @return the SMPPSession
171 */
172 SMPPSession createSMPPSession() {
173 if (configuration.getUsingSSL()) {
174 return new SMPPSession(new SynchronizedPDUSender(new DefaultPDUSender(
175 new DefaultComposer())), new DefaultPDUReader(), SmppSSLConnectionFactory
176 .getInstance());
177 } else {
178 return new SMPPSession();
179 }
180 }
181
182 @Override
183 protected void doStop() throws Exception {
184 LOG.debug("Disconnecting from: " + getEndpoint().getConnectionString() + "...");
185
186 super.doStop();
187 closeSession();
188
189 LOG.info("Disconnected from: " + getEndpoint().getConnectionString());
190 }
191
192 private void closeSession() {
193 if (session != null) {
194 session.removeSessionStateListener(this.sessionStateListener);
195 // remove this hack after http://code.google.com/p/jsmpp/issues/detail?id=93 is fixed
196 try {
197 Thread.sleep(1000);
198 session.unbindAndClose();
199 } catch (Exception e) {
200 LOG.warn("Could not close session " + session);
201 }
202 session = null;
203 }
204 }
205
206 private void reconnect(final long initialReconnectDelay) {
207 if (reconnectLock.tryLock()) {
208 try {
209 Runnable r = new Runnable() {
210 public void run() {
211 boolean reconnected = false;
212
213 LOG.info("Schedule reconnect after " + initialReconnectDelay + " millis");
214 try {
215 Thread.sleep(initialReconnectDelay);
216 } catch (InterruptedException e) {
217 }
218
219 int attempt = 0;
220 while (!(isStopping() || isStopped()) && (session == null || session.getSessionState().equals(SessionState.CLOSED))) {
221 try {
222 LOG.info("Trying to reconnect to " + getEndpoint().getConnectionString() + " - attempt #" + (++attempt) + "...");
223 session = createSession();
224 reconnected = true;
225 } catch (IOException e) {
226 LOG.info("Failed to reconnect to " + getEndpoint().getConnectionString());
227 closeSession();
228 try {
229 Thread.sleep(configuration.getReconnectDelay());
230 } catch (InterruptedException ee) {
231 }
232 }
233 }
234
235 if (reconnected) {
236 LOG.info("Reconnected to " + getEndpoint().getConnectionString());
237 }
238 }
239 };
240
241 Thread t = new Thread(r);
242 t.start();
243 t.join();
244 } catch (InterruptedException e) {
245 // noop
246 } finally {
247 reconnectLock.unlock();
248 }
249 }
250 }
251
252 @Override
253 public String toString() {
254 return "SmppConsumer[" + getEndpoint().getConnectionString() + "]";
255 }
256
257 @Override
258 public SmppEndpoint getEndpoint() {
259 return (SmppEndpoint) super.getEndpoint();
260 }
261
262 /**
263 * Returns the smpp configuration
264 *
265 * @return the configuration
266 */
267 public SmppConfiguration getConfiguration() {
268 return configuration;
269 }
270 }