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.Processor;
023 import org.apache.camel.impl.DefaultConsumer;
024 import org.jsmpp.DefaultPDUReader;
025 import org.jsmpp.DefaultPDUSender;
026 import org.jsmpp.SynchronizedPDUSender;
027 import org.jsmpp.bean.BindType;
028 import org.jsmpp.bean.NumberingPlanIndicator;
029 import org.jsmpp.bean.TypeOfNumber;
030 import org.jsmpp.extra.SessionState;
031 import org.jsmpp.session.BindParameter;
032 import org.jsmpp.session.MessageReceiverListener;
033 import org.jsmpp.session.SMPPSession;
034 import org.jsmpp.session.SessionStateListener;
035 import org.jsmpp.util.DefaultComposer;
036 import org.slf4j.Logger;
037 import org.slf4j.LoggerFactory;
038
039 /**
040 * An implementation of @{link Consumer} which use the SMPP protocol
041 *
042 * @version
043 */
044 public class SmppConsumer extends DefaultConsumer {
045
046 private static final transient Logger LOG = LoggerFactory.getLogger(SmppConsumer.class);
047
048 private SmppConfiguration configuration;
049 private SMPPSession session;
050 private MessageReceiverListener messageReceiverListener;
051 private SessionStateListener sessionStateListener;
052 private final ReentrantLock reconnectLock = new ReentrantLock();
053
054 /**
055 * The constructor which gets a smpp endpoint, a smpp configuration and a
056 * processor
057 */
058 public SmppConsumer(SmppEndpoint endpoint, SmppConfiguration config, Processor processor) {
059 super(endpoint, processor);
060
061 this.configuration = config;
062 this.sessionStateListener = new SessionStateListener() {
063 public void onStateChange(SessionState newState, SessionState oldState, Object source) {
064 if (newState.equals(SessionState.CLOSED)) {
065 LOG.warn("Lost connection to: " + getEndpoint().getConnectionString()
066 + " - trying to reconnect...");
067 closeSession();
068 reconnect(configuration.getInitialReconnectDelay());
069 }
070 }
071 };
072 this.messageReceiverListener = new MessageReceiverListenerImpl(getEndpoint(), getProcessor(), getExceptionHandler());
073 }
074
075 @Override
076 protected void doStart() throws Exception {
077 LOG.debug("Connecting to: " + getEndpoint().getConnectionString() + "...");
078
079 super.doStart();
080 session = createSession();
081
082 LOG.info("Connected to: " + getEndpoint().getConnectionString());
083 }
084
085 private SMPPSession createSession() throws IOException {
086 SMPPSession session = createSMPPSession();
087 session.setEnquireLinkTimer(configuration.getEnquireLinkTimer());
088 session.setTransactionTimer(configuration.getTransactionTimer());
089 session.addSessionStateListener(sessionStateListener);
090 session.setMessageReceiverListener(messageReceiverListener);
091 session.connectAndBind(this.configuration.getHost(), this.configuration.getPort(),
092 new BindParameter(BindType.BIND_RX, this.configuration.getSystemId(),
093 this.configuration.getPassword(), this.configuration.getSystemType(),
094 TypeOfNumber.UNKNOWN, NumberingPlanIndicator.UNKNOWN, ""));
095
096 return session;
097 }
098
099 /**
100 * Factory method to easily instantiate a mock SMPPSession
101 *
102 * @return the SMPPSession
103 */
104 SMPPSession createSMPPSession() {
105 if (configuration.getUsingSSL()) {
106 return new SMPPSession(new SynchronizedPDUSender(new DefaultPDUSender(
107 new DefaultComposer())), new DefaultPDUReader(), SmppSSLConnectionFactory
108 .getInstance());
109 } else {
110 return new SMPPSession();
111 }
112 }
113
114 @Override
115 protected void doStop() throws Exception {
116 LOG.debug("Disconnecting from: " + getEndpoint().getConnectionString() + "...");
117
118 super.doStop();
119 closeSession();
120
121 LOG.info("Disconnected from: " + getEndpoint().getConnectionString());
122 }
123
124 private void closeSession() {
125 if (session != null) {
126 session.removeSessionStateListener(this.sessionStateListener);
127 // remove this hack after http://code.google.com/p/jsmpp/issues/detail?id=93 is fixed
128 try {
129 Thread.sleep(1000);
130 session.unbindAndClose();
131 } catch (Exception e) {
132 LOG.warn("Could not close session " + session);
133 }
134 session = null;
135 }
136 }
137
138 private void reconnect(final long initialReconnectDelay) {
139 if (reconnectLock.tryLock()) {
140 try {
141 Runnable r = new Runnable() {
142 public void run() {
143 boolean reconnected = false;
144
145 LOG.info("Schedule reconnect after " + initialReconnectDelay + " millis");
146 try {
147 Thread.sleep(initialReconnectDelay);
148 } catch (InterruptedException e) {
149 }
150
151 int attempt = 0;
152 while (!(isStopping() || isStopped()) && (session == null || session.getSessionState().equals(SessionState.CLOSED))) {
153 try {
154 LOG.info("Trying to reconnect to " + getEndpoint().getConnectionString() + " - attempt #" + (++attempt) + "...");
155 session = createSession();
156 reconnected = true;
157 } catch (IOException e) {
158 LOG.info("Failed to reconnect to " + getEndpoint().getConnectionString());
159 closeSession();
160 try {
161 Thread.sleep(configuration.getReconnectDelay());
162 } catch (InterruptedException ee) {
163 }
164 }
165 }
166
167 if (reconnected) {
168 LOG.info("Reconnected to " + getEndpoint().getConnectionString());
169 }
170 }
171 };
172
173 Thread t = new Thread(r);
174 t.start();
175 t.join();
176 } catch (InterruptedException e) {
177 // noop
178 } finally {
179 reconnectLock.unlock();
180 }
181 }
182 }
183
184 @Override
185 public String toString() {
186 return "SmppConsumer[" + getEndpoint().getConnectionString() + "]";
187 }
188
189 @Override
190 public SmppEndpoint getEndpoint() {
191 return (SmppEndpoint) super.getEndpoint();
192 }
193
194 /**
195 * Returns the smpp configuration
196 *
197 * @return the configuration
198 */
199 public SmppConfiguration getConfiguration() {
200 return configuration;
201 }
202 }