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 */
047 public class SmppProducer extends DefaultProducer {
048
049 private static final transient Logger LOG = LoggerFactory.getLogger(SmppProducer.class);
050
051 private SmppConfiguration configuration;
052 private SMPPSession session;
053 private SessionStateListener sessionStateListener;
054 private final ReentrantLock connectLock = new ReentrantLock();
055
056 public SmppProducer(SmppEndpoint endpoint, SmppConfiguration config) {
057 super(endpoint);
058 this.configuration = config;
059 this.sessionStateListener = new SessionStateListener() {
060 public void onStateChange(SessionState newState, SessionState oldState, Object source) {
061 if (newState.equals(SessionState.CLOSED)) {
062 LOG.warn("Lost connection to: " + getEndpoint().getConnectionString() + " - trying to reconnect...");
063 closeSession();
064 reconnect(configuration.getInitialReconnectDelay());
065 }
066 }
067 };
068 }
069
070 @Override
071 protected void doStart() throws Exception {
072 super.doStart();
073
074 if (!getConfiguration().isLazySessionCreation()) {
075 if (connectLock.tryLock()) {
076 try {
077 session = createSession();
078 } finally {
079 connectLock.unlock();
080 }
081 }
082 }
083 }
084
085 private SMPPSession createSession() throws IOException {
086 LOG.debug("Connecting to: " + getEndpoint().getConnectionString() + "...");
087
088 SMPPSession session = createSMPPSession();
089 session.setEnquireLinkTimer(this.configuration.getEnquireLinkTimer());
090 session.setTransactionTimer(this.configuration.getTransactionTimer());
091 session.addSessionStateListener(sessionStateListener);
092 session.connectAndBind(
093 this.configuration.getHost(),
094 this.configuration.getPort(),
095 new BindParameter(
096 BindType.BIND_TX,
097 this.configuration.getSystemId(),
098 this.configuration.getPassword(),
099 this.configuration.getSystemType(),
100 TypeOfNumber.valueOf(configuration.getTypeOfNumber()),
101 NumberingPlanIndicator.valueOf(configuration.getNumberingPlanIndicator()),
102 ""));
103
104 LOG.info("Connected to: " + getEndpoint().getConnectionString());
105
106 return session;
107 }
108
109 /**
110 * Factory method to easily instantiate a mock SMPPSession
111 *
112 * @return the SMPPSession
113 */
114 SMPPSession createSMPPSession() {
115 if (configuration.getUsingSSL()) {
116 return new SMPPSession(new SynchronizedPDUSender(new DefaultPDUSender(new DefaultComposer())),
117 new DefaultPDUReader(), SmppSSLConnectionFactory.getInstance());
118 } else {
119 return new SMPPSession();
120 }
121 }
122
123 public void process(Exchange exchange) throws Exception {
124 if (session == null) {
125 if (getConfiguration().isLazySessionCreation()) {
126 if (connectLock.tryLock()) {
127 try {
128 if (session == null) {
129 session = createSession();
130 }
131 } finally {
132 connectLock.unlock();
133 }
134 }
135 }
136 }
137
138 LOG.debug("Sending a short message for exchange id '{}'...", exchange.getExchangeId());
139
140 // only possible by trying to reconnect
141 if (this.session == null) {
142 throw new IOException("Lost connection to " + getEndpoint().getConnectionString() + " and yet not reconnected");
143 }
144
145 SubmitSm submitSm = getEndpoint().getBinding().createSubmitSm(exchange);
146 String messageId = session.submitShortMessage(
147 submitSm.getServiceType(),
148 TypeOfNumber.valueOf(submitSm.getSourceAddrTon()),
149 NumberingPlanIndicator.valueOf(submitSm.getSourceAddrNpi()),
150 submitSm.getSourceAddr(),
151 TypeOfNumber.valueOf(submitSm.getDestAddrTon()),
152 NumberingPlanIndicator.valueOf(submitSm.getDestAddrNpi()),
153 submitSm.getDestAddress(),
154 new ESMClass(),
155 submitSm.getProtocolId(),
156 submitSm.getPriorityFlag(),
157 submitSm.getScheduleDeliveryTime(),
158 submitSm.getValidityPeriod(),
159 new RegisteredDelivery(submitSm.getRegisteredDelivery()),
160 submitSm.getReplaceIfPresent(),
161 new GeneralDataCoding(
162 false,
163 true,
164 MessageClass.CLASS1,
165 Alphabet.valueOf(submitSm.getDataCoding())),
166 (byte) 0,
167 submitSm.getShortMessage(),
168 submitSm.getOptionalParametes());
169
170 LOG.debug("Sent a short message for exchange id '{}' and received message id '{}'",
171 exchange.getExchangeId(), messageId);
172
173 if (exchange.getPattern().isOutCapable()) {
174 LOG.debug("Exchange is out capable, setting headers on out exchange...");
175 exchange.getOut().setHeader(SmppBinding.ID, messageId);
176 } else {
177 LOG.debug("Exchange is not out capable, setting headers on in exchange...");
178 exchange.getIn().setHeader(SmppBinding.ID, messageId);
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 (connectLock.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 connectLock.unlock();
248 }
249 }
250 }
251
252 @Override
253 public SmppEndpoint getEndpoint() {
254 return (SmppEndpoint) super.getEndpoint();
255 }
256
257 /**
258 * Returns the smppConfiguration for this producer
259 *
260 * @return the configuration
261 */
262 public SmppConfiguration getConfiguration() {
263 return configuration;
264 }
265
266 @Override
267 public String toString() {
268 return "SmppProducer[" + getEndpoint().getConnectionString() + "]";
269 }
270 }