PageRenderTime 34ms CodeModel.GetById 9ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/protocols/smpp/src/main/java/org/mobicents/protocols/smpp/Session.java

http://mobicents.googlecode.com/
Java | 411 lines | 338 code | 44 blank | 29 comment | 57 complexity | 378b50224ddc38ea07da6183a5038dfe MD5 | raw file
  1/*
  2 * JBoss, Home of Professional Open Source
  3 * Copyright 2011, Red Hat, Inc. and individual contributors
  4 * by the @authors tag. See the copyright.txt in the distribution for a
  5 * full listing of individual contributors.
  6 *
  7 * This is free software; you can redistribute it and/or modify it
  8 * under the terms of the GNU Lesser General Public License as
  9 * published by the Free Software Foundation; either version 2.1 of
 10 * the License, or (at your option) any later version.
 11 *
 12 * This software is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15 * Lesser General Public License for more details.
 16 *
 17 * You should have received a copy of the GNU Lesser General Public
 18 * License along with this software; if not, write to the Free
 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 21 */
 22
 23package org.mobicents.protocols.smpp;
 24
 25import java.io.IOException;
 26import java.net.UnknownHostException;
 27import java.util.Collection;
 28import java.util.concurrent.atomic.AtomicInteger;
 29import java.util.concurrent.atomic.AtomicReference;
 30
 31import org.slf4j.Logger;
 32import org.slf4j.LoggerFactory;
 33
 34import org.mobicents.protocols.smpp.event.EventDispatcher;
 35import org.mobicents.protocols.smpp.event.SessionObserver;
 36import org.mobicents.protocols.smpp.event.SimpleEventDispatcher;
 37import org.mobicents.protocols.smpp.message.Bind;
 38import org.mobicents.protocols.smpp.message.BindReceiver;
 39import org.mobicents.protocols.smpp.message.BindResp;
 40import org.mobicents.protocols.smpp.message.BindTransceiver;
 41import org.mobicents.protocols.smpp.message.BindTransmitter;
 42import org.mobicents.protocols.smpp.message.CommandId;
 43import org.mobicents.protocols.smpp.message.SMPPPacket;
 44import org.mobicents.protocols.smpp.message.Unbind;
 45import org.mobicents.protocols.smpp.message.UnbindResp;
 46import org.mobicents.protocols.smpp.message.tlv.Tag;
 47import org.mobicents.protocols.smpp.net.SmscLink;
 48import org.mobicents.protocols.smpp.net.TcpLink;
 49import org.mobicents.protocols.smpp.util.APIConfig;
 50import org.mobicents.protocols.smpp.util.APIConfigFactory;
 51import org.mobicents.protocols.smpp.util.DefaultSequenceScheme;
 52import org.mobicents.protocols.smpp.util.PropertyNotFoundException;
 53import org.mobicents.protocols.smpp.util.SequenceNumberScheme;
 54import org.mobicents.protocols.smpp.version.SMPPVersion;
 55import org.mobicents.protocols.smpp.version.VersionException;
 56import org.mobicents.protocols.smpp.version.VersionFactory;
 57
 58// TODO documentation!
 59/**
 60 * @author amit bhayani
 61 * @author orank
 62 */
 63public class Session {
 64    private static final AtomicInteger SESSION_ID = new AtomicInteger(1);
 65    
 66    private final Logger log;
 67    private String sessionId;
 68    private SMPPVersion version = VersionFactory.getDefaultVersion();
 69    private SessionType type;
 70    private AtomicReference<SessionState> state =
 71        new AtomicReference<SessionState>(SessionState.UNBOUND);
 72    private SmscLink smscLink;
 73    private SequenceNumberScheme numberScheme = new DefaultSequenceScheme();
 74    private EventDispatcher eventDispatcher;
 75    private Receiver receiver;
 76    private boolean useOptionalParams = version.isSupportTLV();
 77    private boolean validating = true;
 78    
 79    public Session(SmscLink link) {
 80        sessionId = "Session-" + SESSION_ID.getAndIncrement();
 81        log = LoggerFactory.getLogger(Session.class + "." + sessionId);
 82        this.smscLink = link;
 83        initFromConfig();
 84    }
 85    
 86    public Session(String host, int port) throws UnknownHostException {
 87        this(new TcpLink(host, port));
 88    }
 89
 90    public String getSessionId() {
 91        return sessionId;
 92    }
 93    
 94    public void addObserver(SessionObserver observer) {
 95        eventDispatcher.addObserver(observer);
 96    }
 97    
 98    public void removeObserver(SessionObserver observer) {
 99        eventDispatcher.removeObserver(observer);
100    }
101    
102    public SmscLink getSmscLink() {
103        return smscLink;
104    }
105    
106    public EventDispatcher getEventDispatcher() {
107        return eventDispatcher;
108    }
109    
110    public void setEventDispatcher(EventDispatcher eventDispatcher) {
111        EventDispatcher oldDispatcher = this.eventDispatcher;
112        initNewDispatcher(oldDispatcher, eventDispatcher);
113        this.eventDispatcher = eventDispatcher;
114        if(oldDispatcher!=null){
115        	oldDispatcher.destroy();
116        }
117    }
118    
119    public SMPPVersion getVersion() {
120        return version;
121    }
122
123    public void setVersion(SMPPVersion version) {
124        this.version = version;
125        this.useOptionalParams = version.isSupportTLV();
126    }
127
128    public SequenceNumberScheme getSequenceNumberScheme() {
129        return numberScheme;
130    }
131    
132    public void setSequenceNumberScheme(SequenceNumberScheme numberScheme) {
133        this.numberScheme = numberScheme;
134    }
135    
136    public boolean isValidating() {
137        return validating;
138    }
139
140    public void setValidating(boolean validating) {
141        this.validating = validating;
142    }
143
144    public void bind(SessionType type,
145            String systemID,
146            String password,
147            String systemType) throws IOException {
148        bind(type, systemID, password, systemType, 0, 0, null);
149    }
150    
151    public void bind(SessionType type,
152            String systemID,
153            String password,
154            String systemType,
155            int typeOfNumber,
156            int numberPlanIndicator,
157            String addressRange) throws IOException {
158        Bind bindRequest;
159        if (type == SessionType.TRANSMITTER) {
160            bindRequest = new BindTransmitter();
161        } else if (type == SessionType.RECEIVER) {
162            bindRequest = new BindReceiver();
163        } else {
164            bindRequest = new BindTransceiver();
165        }
166        bindRequest.setVersion(version);
167        bindRequest.setSystemId(systemID);
168        bindRequest.setPassword(password);
169        bindRequest.setSystemType(systemType);
170        bindRequest.setAddressTon(typeOfNumber);
171        bindRequest.setAddressNpi(numberPlanIndicator);
172        bindRequest.setAddressRange(addressRange);
173        bind(bindRequest);
174    }
175    
176    public void bind(Bind bindRequest) throws IOException {
177        if (receiver == null) {
178            initReceiver();
179        }
180        if (getState() != SessionState.UNBOUND) {
181            throw new IllegalStateException("Already binding or bound.");
182        }
183        if (bindRequest.getCommandId() == CommandId.BIND_TRANSMITTER) {
184            type = SessionType.TRANSMITTER;
185        } else if (bindRequest.getCommandId() == CommandId.BIND_RECEIVER) {
186            type = SessionType.RECEIVER;
187        } else {
188            type = SessionType.TRANSCEIVER;
189        }
190        if (!smscLink.isConnected()) {
191            smscLink.connect();
192        }
193        setLinkTimeout(APIConfig.BIND_TIMEOUT);
194        log.debug("Sending bind packet to the SMSC..");
195        sendPacketInternal(bindRequest);
196        receiver.start();
197    }
198
199    public void unbind() throws IOException {
200        sendPacketInternal(new Unbind());
201    }
202    
203    public void sendPacket(SMPPPacket packet) throws IOException {
204        int commandId = packet.getCommandId();
205        switch (commandId) {
206        case CommandId.BIND_TRANSMITTER:
207        case CommandId.BIND_TRANSCEIVER:
208        case CommandId.BIND_RECEIVER:
209            bind((Bind) packet);
210            return;
211        }
212        if (type == SessionType.RECEIVER) {
213            // We allow the receiver to send any response type but a very
214            // limited set of requests.
215            if (packet.isRequest()
216                    && !(commandId == CommandId.UNBIND
217                            || commandId == CommandId.ENQUIRE_LINK)) {
218                throw new UnsupportedOperationException(
219                        "Receiver connection cannot send command " + commandId);
220            }
221        }
222        sendPacketInternal(packet);
223    }
224
225    public void closeLink() throws IOException {
226        if (getState() == SessionState.UNBOUND || getState() == SessionState.UNBINDING) {
227            smscLink.disconnect();
228        } else {
229            throw new IllegalStateException("Cannot close link while connection is bound.");
230        }
231    }
232    
233    public SessionState getState() {
234        return state.get();
235    }
236
237    public Receiver getReceiver(Receiver receiver) {
238        return receiver;
239    }
240    
241    public void setReceiver(Receiver receiver) {
242        if (this.receiver != null && this.receiver.isStarted()) {
243            throw new IllegalStateException(
244                    "Cannot change the receiver while it's running");
245        }
246        this.receiver = receiver;
247    }
248    
249    public void processReceivedPacket(SMPPPacket packet) {
250        switch (packet.getCommandId()) {
251        case CommandId.BIND_TRANSMITTER_RESP:
252        case CommandId.BIND_RECEIVER_RESP:
253        case CommandId.BIND_TRANSCEIVER_RESP:
254            processReceivedBindResponse((BindResp) packet);
255            break;
256        case CommandId.UNBIND:
257            processReceivedUnbind((Unbind) packet);
258            break;
259        case CommandId.UNBIND_RESP:
260            processReceivedUnbindResponse((UnbindResp) packet);
261            break;
262        default:
263            // Do nothing.
264        }
265    }
266
267    private void setState(SessionState fromState, SessionState toState) {
268        if (!state.compareAndSet(fromState, toState)) {
269            log.error("Race condition in setting state - expected {} but is {}. New value is "+ toState,
270                    fromState, getState());
271        }
272    }
273
274    private void initFromConfig() {
275        APIConfig config = APIConfigFactory.getConfig();
276        EventDispatcher dispatcher;
277        try {
278            dispatcher = config.getClassInstance(
279                    APIConfig.EVENT_DISPATCHER_CLASS, EventDispatcher.class);
280        } catch (PropertyNotFoundException x) {
281            log.debug("Config does not specify an event dispatcher. Using {}",
282                    SimpleEventDispatcher.class);
283            dispatcher = new SimpleEventDispatcher();
284        }
285        setEventDispatcher(dispatcher);
286    }
287
288    private void initReceiver() {
289        receiver = new ReceiverThread(this);
290        receiver.setName(sessionId + "-Receiver");
291    }
292
293    private void initNewDispatcher(EventDispatcher oldDispatcher, EventDispatcher newDispatcher) {
294        newDispatcher.init();
295        if (oldDispatcher != null) {
296            Collection<SessionObserver> observers =
297                oldDispatcher.getObservers();
298            for (SessionObserver observer : observers) {
299                newDispatcher.addObserver(observer);
300            }
301        }
302    }
303    
304    private void sendPacketInternal(SMPPPacket packet) throws IOException {
305        if (packet.getSequenceNum() < 0L && numberScheme != null) {
306            packet.setSequenceNum(numberScheme.nextNumber());
307        }
308        if (validating) {
309            packet.validate(version);
310        }
311        smscLink.write(packet, useOptionalParams);
312        processSentPacket(packet);
313    }
314
315    private void processSentPacket(SMPPPacket packet) {
316        switch (packet.getCommandId()) {
317        case CommandId.BIND_TRANSMITTER:
318        case CommandId.BIND_RECEIVER:
319        case CommandId.BIND_TRANSCEIVER:
320            processSentBind((Bind) packet);
321            break;
322        case CommandId.UNBIND:
323            processSentUnbind((Unbind) packet);
324            break;
325        case CommandId.UNBIND_RESP:
326            processSentUnbindResponse((UnbindResp) packet);
327            break;
328        default:
329        }
330    }
331    
332    private void processSentBind(Bind bindRequest) {
333        setState(SessionState.UNBOUND, SessionState.BINDING);
334    }
335    
336    private void processSentUnbind(Unbind unbindRequest) {
337        setState(SessionState.BOUND, SessionState.UNBINDING);
338    }
339    
340    private void processSentUnbindResponse(UnbindResp unbindResponse) {
341        int status = unbindResponse.getCommandStatus();
342        if (status == 0) {
343            setState(SessionState.UNBINDING, SessionState.UNBOUND);
344        }
345    }
346    
347    private void processReceivedBindResponse(BindResp bindResponse) {
348        int status = bindResponse.getCommandStatus();
349        if (status == 0) {
350            setState(SessionState.BINDING, SessionState.BOUND);
351            negotiateVersion(bindResponse);
352            setLinkTimeout(APIConfig.LINK_TIMEOUT);
353        } else {
354            log.warn("Received a bind response with status {}", status);
355            setState(SessionState.BINDING, SessionState.UNBOUND);
356        }
357    }
358    
359    private void negotiateVersion(BindResp bindResponse) {
360        if (!bindResponse.isSet(Tag.SC_INTERFACE_VERSION)) {
361            log.info("SMSC did not supply SC_INTERFACE_VERSION."
362                    + " Disabling optional parameter support.");
363            useOptionalParams = false;
364            return;
365        }
366        int versionId = 0;
367        try {
368            versionId =
369                bindResponse.getTLVTable().getInt(Tag.SC_INTERFACE_VERSION);
370            SMPPVersion smscVersion =
371                VersionFactory.getVersion(versionId);
372            log.info("SMSC states its version as {}", smscVersion);
373            if (smscVersion.isOlderThan(version)) {
374                version = smscVersion;
375                useOptionalParams = version.isSupportTLV();
376            }
377        } catch (VersionException x) {
378            log.debug("SMSC implements a version I don't know: {}", versionId);
379        }
380    }
381    
382    private void processReceivedUnbind(Unbind unbindRequest) {
383        setState(SessionState.BOUND, SessionState.UNBINDING);
384    }
385    
386    private void processReceivedUnbindResponse(UnbindResp unbindResponse) {
387        int status = unbindResponse.getCommandStatus();
388        if (status == 0) {
389            setState(SessionState.UNBINDING, SessionState.UNBOUND);
390        } else {
391            log.warn("Received an unbind response with status {}", status);
392        }
393    }
394    
395    private void setLinkTimeout(String propName) {
396        try {
397            if (smscLink.isTimeoutSupported()) {
398                APIConfig config = APIConfigFactory.getConfig();
399                int linkTimeout = config.getInt(propName);
400                smscLink.setTimeout(linkTimeout);
401                if (log.isDebugEnabled()) {
402                    log.debug("Set the link timeout to {}", linkTimeout);
403                }
404            } else {
405                log.info("SMSC link implementation does not support timeouts.");
406            }
407        } catch (PropertyNotFoundException x) {
408            log.debug("Not setting link timeout as it is not configured.");
409        }
410    }
411}