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    }