package org.mobicents.tools.smpp.multiplexer; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.apache.log4j.Logger; import org.mobicents.tools.heartbeat.api.Node; import org.mobicents.tools.sip.balancer.BalancerRunner; import org.mobicents.tools.sip.balancer.InvocationContext; import com.cloudhopper.smpp.SmppConstants; import com.cloudhopper.smpp.pdu.BaseBind; import com.cloudhopper.smpp.pdu.BaseBindResp; import com.cloudhopper.smpp.pdu.Pdu; import com.cloudhopper.smpp.pdu.Unbind; import com.cloudhopper.smpp.tlv.Tlv; public class UserSpace { private static final Logger logger = Logger.getLogger(UserSpace.class); public enum BINDSTATE { INITIAL, OPEN, BINDING, BOUND, REBINDING, UNBINDING, CLOSED } private String systemId; private String password; private String systemType; private ConcurrentLinkedQueue<MServerConnectionImpl> pendingCustomers; private ConcurrentHashMap<Long, MServerConnectionImpl> customers; private ConcurrentHashMap<Long, MClientConnectionImpl> connectionsToServers; private InvocationContext ctx = null; private ScheduledFuture<?> reconnectionSchedule = null; private MBinderRunnable reconnectTask = null; private ConcurrentHashMap<Long, ScheduledFuture<MBinderRunnable>> mapReconnectionSchedule = new ConcurrentHashMap<>(); private Node [] nodes; private Long serverSessionID = new Long(0); private ScheduledExecutorService monitorExecutor; private long reconnectPeriod; private BalancerRunner balancerRunner; private MBalancerDispatcher dispatcher; private AtomicReference<BINDSTATE> bindState=new AtomicReference<BINDSTATE>(BINDSTATE.INITIAL); private AtomicReference<MServerConnectionImpl> bindingCustomer=new AtomicReference<MServerConnectionImpl>(); public UserSpace(String systemId, String password, Node[] nodes, BalancerRunner balancerRunner,ScheduledExecutorService monitorExecutor, MBalancerDispatcher dispatcher) { this.systemId = systemId; this.password = password; this.balancerRunner = balancerRunner; this.customers = new ConcurrentHashMap<Long, MServerConnectionImpl>(); this.ctx = balancerRunner.getLatestInvocationContext(); this.pendingCustomers = new ConcurrentLinkedQueue<MServerConnectionImpl>(); this.connectionsToServers = new ConcurrentHashMap<Long, MClientConnectionImpl>(); this.nodes = nodes; this.monitorExecutor = monitorExecutor; this.dispatcher = dispatcher; this.reconnectPeriod = balancerRunner.balancerContext.lbConfig.getSmppConfiguration().getReconnectPeriod(); } public synchronized void bind(MServerConnectionImpl customer,Pdu bindPdu) { switch(bindState.get()) { case INITIAL: bindState.set(BINDSTATE.OPEN); if(logger.isDebugEnabled()) logger.debug("Initial bind, bindstate open"); initBind(customer); break; case OPEN: case BINDING: if(logger.isDebugEnabled()) logger.debug("Put customer with sessionId : "+ customer.getSessionId() +" to pendingCustomers"); this.pendingCustomers.offer(customer); break; case BOUND: case CLOSED: case REBINDING: case UNBINDING: if(logger.isDebugEnabled()) logger.debug("Validate customer"); validateConnection(customer); break; } } void bindSuccesfull(Node node, Long serverSessionID) { ScheduledFuture<MBinderRunnable> currReconnectionSchedule = mapReconnectionSchedule.get(serverSessionID); if(currReconnectionSchedule!=null) currReconnectionSchedule.cancel(true); bindState.set(BINDSTATE.BOUND); MServerConnectionImpl customer=this.bindingCustomer.get(); if(logger.isDebugEnabled()) logger.debug("Init bind successful for customer with session ID : " + customer.getSessionId() + " to server " + node.getIp()+":"+node.getProperties().get("smppPort")); validateConnection(customer); customer=this.pendingCustomers.poll(); while(customer!=null) { validateConnection(customer); customer=this.pendingCustomers.poll(); } } void bindFailed(Long serverSessionID, Pdu packet) { MClientConnectionImpl clientConnection = this.connectionsToServers.get(serverSessionID); if(logger.isDebugEnabled()) logger.debug("Bind failed to server with serverSessionId : " + serverSessionID); //in case no new customer set state to INITIAL if(packet.getCommandStatus()!=SmppConstants.STATUS_INVPASWD&&packet.getCommandStatus()!=SmppConstants.STATUS_INVSYSID) { //in case its not password try to reinint if(!mapReconnectionSchedule.containsKey(serverSessionID)) { reconnectTask = new MBinderRunnable(clientConnection, systemId, password, systemType); reconnectionSchedule = monitorExecutor.scheduleAtFixedRate(reconnectTask, reconnectPeriod, reconnectPeriod, TimeUnit.MILLISECONDS); mapReconnectionSchedule.put(serverSessionID, (ScheduledFuture<MBinderRunnable>) reconnectionSchedule); } } else { //otherwise remove from list this.connectionsToServers.remove(serverSessionID); //in case zero left send unbind to curr customer if(this.connectionsToServers.isEmpty()) { bindingCustomer.get().sendBindResponse(packet); MServerConnectionImpl customer=this.pendingCustomers.poll(); if(customer!=null) initBind(customer); } } } private void validateConnection(MServerConnectionImpl customer) { if(logger.isDebugEnabled()) logger.debug("Validate connection for customer with sessionId : " + customer.getSessionId()); BaseBindResp bindResponse = (BaseBindResp)customer.getBindRequest().createResponse(); bindResponse.setSystemId("loadbalancer"); if (customer.getConfig().getInterfaceVersion() >= SmppConstants.VERSION_3_4 && ((BaseBind<?>) customer.getBindRequest()).getInterfaceVersion() >= SmppConstants.VERSION_3_4) { Tlv scInterfaceVersion = new Tlv(SmppConstants.TAG_SC_INTERFACE_VERSION, new byte[] { customer.getConfig().getInterfaceVersion() }); bindResponse.addOptionalParameter(scInterfaceVersion); } if(!(customer.getConfig().getPassword().equals(this.password))) { if(logger.isDebugEnabled()) logger.debug("LB sending fail bind response for customer with sessionId : " + customer.getSessionId()); //SEND BIND FAILED TO CUSTOMER bindResponse.setCommandStatus(SmppConstants.STATUS_INVPASWD); customer.sendBindResponse(bindResponse); } else { if(logger.isDebugEnabled()) logger.debug("LB sending successful bind response for customer with sessionId : " + customer.getSessionId()); this.customers.put(customer.getSessionId(), customer); //SEND BIND SUCCESFULL TO CUSTOMER bindResponse.setCommandStatus(SmppConstants.STATUS_OK); customer.sendBindResponse(bindResponse); } } public void initBind(MServerConnectionImpl customer) { boolean isSslConnection = customer.getConfig().isUseSsl(); this.systemType = customer.getConfig().getSystemType(); if(logger.isDebugEnabled()) logger.debug("Start initial bind for customer with sessionID : " + customer.getSessionId()); bindingCustomer.set(customer); //INITIAL CONNECTION TO ALL SERVERS for(Node node: nodes) { if(balancerRunner.balancerContext.terminateTLSTraffic) isSslConnection = false; connectionsToServers.put(serverSessionID, new MClientConnectionImpl(serverSessionID, this, monitorExecutor, balancerRunner, node, isSslConnection)); monitorExecutor.execute(new MBinderRunnable(connectionsToServers.get(serverSessionID), systemId, password, systemType)); serverSessionID++; } } public void unbind(Long sessionId, Unbind packet) { if(logger.isDebugEnabled()) logger.debug("LB sending unbind response for customer with sessionId : " + sessionId + " and remove it. Current size of customers is : " + customers.size()); //statistic balancerRunner.balancerContext.smppRequestsToServer.getAndIncrement(); balancerRunner.incMessages(); balancerRunner.balancerContext.smppRequestsProcessedById.get(packet.getCommandId()).incrementAndGet(); customers.get(sessionId).sendUnbindResponse(packet.createResponse()); customers.remove(sessionId); if(customers.isEmpty()) { if(logger.isDebugEnabled()) logger.debug("No more connected customers we send unbind to server with serverSessionID "+serverSessionID); for(Long serverSessionID :connectionsToServers.keySet()) connectionsToServers.get(serverSessionID).sendUnbindRequest(packet); dispatcher.getUserSpaces().remove(systemId); } } public void disconnectFromServer() { for(Entry<Long, ScheduledFuture<MBinderRunnable>> entry : mapReconnectionSchedule.entrySet()) entry.getValue().cancel(true); for(Long serverSessionID :connectionsToServers.keySet()) connectionsToServers.get(serverSessionID).sendUnbindRequest(new Unbind()); dispatcher.getUserSpaces().remove(systemId); } public void sendRequestToServer(Long sessionId, Pdu packet) { //statistic balancerRunner.balancerContext.smppRequestsToServer.getAndIncrement(); balancerRunner.incMessages(); balancerRunner.balancerContext.smppRequestsProcessedById.get(packet.getCommandId()).incrementAndGet(); balancerRunner.balancerContext.smppBytesToServer.addAndGet(packet.getCommandLength()); if(logger.isDebugEnabled()) logger.debug("LB sending message form customer with sessionId : " + sessionId + " to provider "); ctx.smppToProviderBalancerAlgorithm.processSubmitToProvider(connectionsToServers, sessionId, packet); } public void sendRequestToClient(Pdu packet, Long serverSessionId) { balancerRunner.balancerContext.smppRequestsToClient.getAndIncrement(); balancerRunner.incMessages(); balancerRunner.balancerContext.smppRequestsProcessedById.get(packet.getCommandId()).incrementAndGet(); balancerRunner.balancerContext.smppBytesToClient.addAndGet(packet.getCommandLength()); if(logger.isDebugEnabled()) logger.debug("LB sending request from SMPP provider with sessionId : " + serverSessionId + " to Node."); ctx.smppToNodeBalancerAlgorithm.processSubmitToNode(customers,serverSessionId,packet); } public void sendResponseToServer(Long sessionId, Pdu packet,Long serverSessionId) { if(logger.isDebugEnabled()) logger.debug("LB sending response from customer with sessionId : " + sessionId + " to server "); //statistic balancerRunner.balancerContext.smppResponsesProcessedById.get(packet.getCommandId()).incrementAndGet(); balancerRunner.balancerContext.smppBytesToServer.addAndGet(packet.getCommandLength()); if(serverSessionId!=null) connectionsToServers.get(serverSessionId).sendSmppResponse(packet); } public void sendResponseToClient(CustomerPacket customerPacket, Pdu packet) { //stistic balancerRunner.balancerContext.smppResponsesProcessedById.get(packet.getCommandId()).incrementAndGet(); balancerRunner.balancerContext.smppBytesToClient.addAndGet(packet.getCommandLength()); packet.setSequenceNumber(customerPacket.getSequence()); if(logger.isDebugEnabled()) logger.debug("LB sending response form server to client with sequence : " + packet.getSequenceNumber()); if(packet.getCommandId()==SmppConstants.CMD_ID_ENQUIRE_LINK_RESP) customers.get(customerPacket.getSessionId()).updateLastTimeSMPPLinkUpdated(); else customers.get(customerPacket.getSessionId()).sendResponse(packet); } public void enquireLinkReceivedFromServer() { for(Long key:customers.keySet()) customers.get(key).updateLastTimeSMPPLinkUpdated(); } public void unbindRequestedFromServer(Unbind packet, Long serverSessionId) { if(logger.isDebugEnabled()) logger.debug("LB got unbind request from server with serverSessionId :" + serverSessionId+". LB removed it from list of nodes"); connectionsToServers.remove(serverSessionId); if(connectionsToServers.isEmpty()) { if(logger.isDebugEnabled()) logger.debug("LB hasn't had nodes already : " + serverSessionId+". LB sent unbind to all clients and unbind response to server"); for(Long key:customers.keySet()) customers.get(key).sendUnbindRequest(packet); MClientConnectionImpl mClientConnectionImpl = connectionsToServers.get(serverSessionId); if(logger.isDebugEnabled()) logger.debug("clientConnection " + mClientConnectionImpl + " for session " + serverSessionId); if(mClientConnectionImpl != null) { mClientConnectionImpl.sendUnbindResponse(packet.createResponse()); } } } public void connectionLost(Long serverSessionID) { //set all connected customers to rebinding state and trying to reconnect if(logger.isDebugEnabled()) logger.debug("LB will start rebind task with period : " + reconnectPeriod); if(connectionsToServers.size() == 1) for(Long key:customers.keySet()) customers.get(key).reconnectState(true); MClientConnectionImpl connection = connectionsToServers.get(serverSessionID); connectionsToServers.remove(serverSessionID); if(!mapReconnectionSchedule.containsKey(serverSessionID)) { reconnectTask = new MBinderRunnable(connection, systemId, password, systemType); reconnectionSchedule = monitorExecutor.scheduleAtFixedRate(reconnectTask, reconnectPeriod, reconnectPeriod, TimeUnit.MILLISECONDS); mapReconnectionSchedule.put(serverSessionID, (ScheduledFuture<MBinderRunnable>) reconnectionSchedule); } } public void reconnectSuccesful(Long serverSessionID, MClientConnectionImpl connection) { mapReconnectionSchedule.get(serverSessionID).cancel(true); for(Long key:customers.keySet()) customers.get(key).reconnectState(false); connectionsToServers.put(serverSessionID,connection); } public Map<Long, MServerConnectionImpl> getCustomers() { return customers; } public MBalancerDispatcher getDispatcher() { return dispatcher; } }