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.impl.DefaultProducer;
024 import org.jsmpp.DefaultPDUReader;
025 import org.jsmpp.DefaultPDUSender;
026 import org.jsmpp.SynchronizedPDUSender;
027 import org.jsmpp.bean.Alphabet;
028 import org.jsmpp.bean.BindType;
029 import org.jsmpp.bean.ESMClass;
030 import org.jsmpp.bean.GeneralDataCoding;
031 import org.jsmpp.bean.MessageClass;
032 import org.jsmpp.bean.NumberingPlanIndicator;
033 import org.jsmpp.bean.RegisteredDelivery;
034 import org.jsmpp.bean.SubmitSm;
035 import org.jsmpp.bean.TypeOfNumber;
036 import org.jsmpp.extra.SessionState;
037 import org.jsmpp.session.BindParameter;
038 import org.jsmpp.session.SMPPSession;
039 import org.jsmpp.session.SessionStateListener;
040 import org.jsmpp.util.DefaultComposer;
041 import org.slf4j.Logger;
042 import org.slf4j.LoggerFactory;
043
044 /**
045 * An implementation of @{link Producer} which use the SMPP protocol
046 * @version
047 * @author muellerc
048 */
049 public class SmppProducer extends DefaultProducer {
050
051 private static final transient Logger LOG = LoggerFactory.getLogger(SmppProducer.class);
052
053 private SmppConfiguration configuration;
054 private SMPPSession session;
055 private SessionStateListener sessionStateListener;
056 private final ReentrantLock reconnectLock = new ReentrantLock();
057
058 public SmppProducer(SmppEndpoint endpoint, SmppConfiguration config) {
059 super(endpoint);
060 this.configuration = config;
061 this.sessionStateListener = new SessionStateListener() {
062 public void onStateChange(SessionState newState, SessionState oldState, Object source) {
063 if (newState.equals(SessionState.CLOSED)) {
064 LOG.warn("Lost connection to: " + getEndpoint().getConnectionString() + " - trying to reconnect...");
065 closeSession();
066 reconnect(configuration.getInitialReconnectDelay());
067 }
068 }
069 };
070 }
071
072 @Override
073 protected void doStart() throws Exception {
074 LOG.debug("Connecting to: " + getEndpoint().getConnectionString() + "...");
075
076 super.doStart();
077 session = createSession();
078
079 LOG.info("Connected to: " + getEndpoint().getConnectionString());
080 }
081
082 private SMPPSession createSession() throws IOException {
083 SMPPSession session = createSMPPSession();
084 session.setEnquireLinkTimer(this.configuration.getEnquireLinkTimer());
085 session.setTransactionTimer(this.configuration.getTransactionTimer());
086 session.addSessionStateListener(sessionStateListener);
087 session.connectAndBind(
088 this.configuration.getHost(),
089 this.configuration.getPort(),
090 new BindParameter(
091 BindType.BIND_TX,
092 this.configuration.getSystemId(),
093 this.configuration.getPassword(),
094 this.configuration.getSystemType(),
095 TypeOfNumber.valueOf(configuration.getTypeOfNumber()),
096 NumberingPlanIndicator.valueOf(configuration.getNumberingPlanIndicator()),
097 ""));
098
099 return session;
100 }
101
102 /**
103 * Factory method to easily instantiate a mock SMPPSession
104 *
105 * @return the SMPPSession
106 */
107 SMPPSession createSMPPSession() {
108 if (configuration.getUsingSSL()) {
109 return new SMPPSession(new SynchronizedPDUSender(new DefaultPDUSender(new DefaultComposer())),
110 new DefaultPDUReader(), SmppSSLConnectionFactory.getInstance());
111 } else {
112 return new SMPPSession();
113 }
114 }
115
116 public void process(Exchange exchange) throws Exception {
117 if (LOG.isDebugEnabled()) {
118 LOG.debug("Sending a short message for exchange id '"
119 + exchange.getExchangeId() + "'...");
120 }
121
122 // only possible by trying to reconnect
123 if (this.session == null) {
124 throw new IOException("Lost connection to " + getEndpoint().getConnectionString() + " and yet not reconnected");
125 }
126
127 SubmitSm submitSm = getEndpoint().getBinding().createSubmitSm(exchange);
128 String messageId = session.submitShortMessage(
129 submitSm.getServiceType(),
130 TypeOfNumber.valueOf(submitSm.getSourceAddrTon()),
131 NumberingPlanIndicator.valueOf(submitSm.getSourceAddrNpi()),
132 submitSm.getSourceAddr(),
133 TypeOfNumber.valueOf(submitSm.getDestAddrTon()),
134 NumberingPlanIndicator.valueOf(submitSm.getDestAddrNpi()),
135 submitSm.getDestAddress(),
136 new ESMClass(),
137 submitSm.getProtocolId(),
138 submitSm.getPriorityFlag(),
139 submitSm.getScheduleDeliveryTime(),
140 submitSm.getValidityPeriod(),
141 new RegisteredDelivery(submitSm.getRegisteredDelivery()),
142 submitSm.getReplaceIfPresent(),
143 new GeneralDataCoding(
144 false,
145 true,
146 MessageClass.CLASS1,
147 Alphabet.valueOf(submitSm.getDataCoding())),
148 (byte) 0,
149 submitSm.getShortMessage(),
150 submitSm.getOptionalParametes());
151
152 if (LOG.isDebugEnabled()) {
153 LOG.debug("Sent a short message for exchange id '"
154 + exchange.getExchangeId() + "' and received message id '"
155 + messageId + "'");
156 }
157
158 if (exchange.getPattern().isOutCapable()) {
159 if (LOG.isDebugEnabled()) {
160 LOG.debug("Exchange is out capable, setting headers on out exchange...");
161 }
162 exchange.getOut().setHeader(SmppBinding.ID, messageId);
163 } else {
164 if (LOG.isDebugEnabled()) {
165 LOG.debug("Exchange is not out capable, setting headers on in exchange...");
166 }
167 exchange.getIn().setHeader(SmppBinding.ID, messageId);
168 }
169 }
170
171 @Override
172 protected void doStop() throws Exception {
173 LOG.debug("Disconnecting from: " + getEndpoint().getConnectionString() + "...");
174
175 super.doStop();
176 closeSession();
177
178 LOG.info("Disconnected from: " + getEndpoint().getConnectionString());
179 }
180
181 private void closeSession() {
182 if (session != null) {
183 session.removeSessionStateListener(this.sessionStateListener);
184 // remove this hack after http://code.google.com/p/jsmpp/issues/detail?id=93 is fixed
185 try {
186 Thread.sleep(1000);
187 session.unbindAndClose();
188 } catch (Exception e) {
189 LOG.warn("Could not close session " + session);
190 }
191 session = null;
192 }
193 }
194
195 private void reconnect(final long initialReconnectDelay) {
196 if (reconnectLock.tryLock()) {
197 try {
198 Runnable r = new Runnable() {
199 public void run() {
200 boolean reconnected = false;
201
202 LOG.info("Schedule reconnect after " + initialReconnectDelay + " millis");
203 try {
204 Thread.sleep(initialReconnectDelay);
205 } catch (InterruptedException e) {
206 }
207
208 int attempt = 0;
209 while (!(isStopping() || isStopped()) && (session == null || session.getSessionState().equals(SessionState.CLOSED))) {
210 try {
211 LOG.info("Trying to reconnect to " + getEndpoint().getConnectionString() + " - attempt #" + (++attempt) + "...");
212 session = createSession();
213 reconnected = true;
214 } catch (IOException e) {
215 LOG.info("Failed to reconnect to " + getEndpoint().getConnectionString());
216 closeSession();
217 try {
218 Thread.sleep(configuration.getReconnectDelay());
219 } catch (InterruptedException ee) {
220 }
221 }
222 }
223
224 if (reconnected) {
225 LOG.info("Reconnected to " + getEndpoint().getConnectionString());
226 }
227 }
228 };
229
230 Thread t = new Thread(r);
231 t.start();
232 t.join();
233 } catch (InterruptedException e) {
234 // noop
235 } finally {
236 reconnectLock.unlock();
237 }
238 }
239 }
240
241 @Override
242 public SmppEndpoint getEndpoint() {
243 return (SmppEndpoint) super.getEndpoint();
244 }
245
246 /**
247 * Returns the smppConfiguration for this producer
248 *
249 * @return the configuration
250 */
251 public SmppConfiguration getConfiguration() {
252 return configuration;
253 }
254
255 @Override
256 public String toString() {
257 return "SmppProducer[" + getEndpoint().getConnectionString() + "]";
258 }
259 }