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