Package org.smslib

Source Code of org.smslib.Service

// SMSLib for Java v3
// A Java API library for sending and receiving SMS via a GSM modem
// or other supported gateways.
// Web Site: http://www.smslib.org
//
// Copyright (C) 2002-2012, Thanasis Delenikas, Athens/GREECE.
// SMSLib is distributed under the terms of the Apache License version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.smslib;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.smslib.AGateway.GatewayStatuses;
import org.smslib.InboundMessage.MessageClasses;
import org.smslib.OutboundMessage.FailureCauses;
import org.smslib.OutboundMessage.MessageStatuses;
import org.smslib.balancing.LoadBalancer;
import org.smslib.balancing.RoundRobinLoadBalancer;
import org.smslib.crypto.KeyManager;
import org.smslib.helper.Logger;
import org.smslib.notify.NotifyQueueManager;
import org.smslib.queues.AbstractQueueManager;
import org.smslib.queues.DefaultQueueManager;
import org.smslib.routing.Router;
import org.smslib.threading.AServiceThread;

/**
* This is main library class. Your primary interface with SMSLib is via methods
* defined in this class.
*/
public class Service
{
  public enum ServiceStatus
  {
    STARTING, STARTED, STOPPING, STOPPED
  }

  private List<AGateway> gatewayList;

  private Router router;

  private LoadBalancer loadBalancer;

  private WatchDog watchDog;

  private IInboundMessageNotification inboundMessageNotification;

  private IOutboundMessageNotification outboundMessageNotification;

  private ICallNotification callNotification;

  private IGatewayStatusNotification gatewayStatusNotification;

  private IQueueSendingNotification queueSendingNotification;

  private IOrphanedMessageNotification orphanedMessageNotification;

  protected AbstractQueueManager queueManager;

  protected NotifyQueueManager notifyQueueManager;

  private long startMillis;

  private Collection<Group> groups;

  private volatile ServiceStatus serviceStatus;

  private KeyManager keyManager;

  public Settings S;

  private IUSSDNotification ussdNotification;

  private static final Service service = new Service();

  private Service()
  {
    setSettings(new Settings());
    initializeService();
  }

  protected void initializeService()
  {
    this.startMillis = System.currentTimeMillis();
    setServiceStatus(ServiceStatus.STOPPED);
    this.groups = new ArrayList<Group>();
    listSystemInformation();
    this.gatewayList = new ArrayList<AGateway>();
    this.keyManager = KeyManager.getInstance();
    setRouter(new Router());
    setLoadBalancer(new RoundRobinLoadBalancer());
    setNotifyQueueManager(new NotifyQueueManager());
  }

  private void listSystemInformation()
  {
    Logger.getInstance().logInfo(Library.getLibraryDescription(), null, null);
    Logger.getInstance().logInfo("Version: " + Library.getLibraryVersion(), null, null);
    Logger.getInstance().logInfo("JRE Version: " + System.getProperty("java.version"), null, null);
    Logger.getInstance().logInfo("JRE Impl Version: " + System.getProperty("java.vm.version"), null, null);
    Logger.getInstance().logInfo("O/S: " + System.getProperty("os.name") + " / " + System.getProperty("os.arch") + " / " + System.getProperty("os.version"), null, null);
  }

  public static Service getInstance()
  {
    return Service.service;
  }

  /**
   * Adds a gateway to the list of gateways managed by the Service class. The
   * Service should be stopped in order to add gateways.
   *
   * @param gateway
   *            The gateway to be added.
   * @throws GatewayException
   *             You tried to add a gateway while the Service is started.
   * @see #removeGateway(AGateway)
   * @see #getGateways()
   */
  public void addGateway(AGateway gateway) throws GatewayException
  {
    if (getServiceStatus() != ServiceStatus.STOPPED) throw new GatewayException("Cannot add gateways while Service is running!");
    getGateways().add(gateway);
  }

  /**
   * Removes a gateway from the list of gateways managed by the Service class.
   * The Service should be stopped in order to remove a gateway.
   *
   * @param gateway
   *            The gateway to be removed.
   * @return True if the requested gateway has been removed successfully.
   * @throws GatewayException
   *             You tried to remove a gateway while the Service is started.
   * @see #addGateway(AGateway)
   * @see #getGateways()
   */
  public boolean removeGateway(AGateway gateway) throws GatewayException
  {
    if (getServiceStatus() != ServiceStatus.STOPPED) throw new GatewayException("Cannot remove gateways while Service is running!");
    return getGateways().remove(gateway);
  }

  /**
   * Returns the gateway object based on the provided id. Returns null if no
   * gateway matches the provided id.
   *
   * @param gatewayId
   *            The gateway id to search for.
   * @return The gateway which matches the provided gateway id, or null.
   */
  public AGateway getGateway(String gatewayId)
  {
    for (AGateway gateway : getGateways())
    {
      if (gateway.getGatewayId().equalsIgnoreCase(gatewayId)) return gateway;
    }
    return null;
  }

  /**
   * Initializes and starts the SMSLib service. This should be the first call
   * before you use the Service class for sending/receiving messages. The call
   * will try to start all defined gateways.
   *
   * @throws SMSLibException
   *             No Gateways are defined.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see #stopService()
   */
  public synchronized void startService() throws SMSLibException, TimeoutException, GatewayException, IOException, InterruptedException
  {
    startService(true);
  }

  /**
   * Initializes and starts the SMSLib service. Depending on the parameter,
   * all defined gateways can be requested to start or some may be allowed to
   * fail. This should be the first call before you use the Service class for
   * sending/receiving messages.
   *
   * @param startAllGateways
   *            True if all gateways should be started, False if some gateways
   *            are allowed to fail.
   * @throws SMSLibException
   *             No Gateways are defined.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see #stopService()
   */
  public synchronized void startService(boolean startAllGateways) throws SMSLibException, TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (queueManager == null) queueManager = new DefaultQueueManager();
    try
    {
      getNotifyQueueManager().start();
      getQueueManager().start();
      setServiceStatus(ServiceStatus.STARTING);
      setWatchDog(new WatchDog("WatchDog", (getSettings().WATCHDOG_INTERVAL * 1000)));
      startService_Internal(startAllGateways);
      setServiceStatus(ServiceStatus.STARTED);
    }
    catch (SMSLibException e)
    {
      stopService();
      throw e;
    }
    catch (IOException e)
    {
      stopService();
      throw e;
    }
    catch (InterruptedException e)
    {
      stopService();
      throw e;
    }
  }

  private void startService_Internal(boolean startAll) throws SMSLibException, TimeoutException, GatewayException, IOException, InterruptedException
  {
    class ExceptionPair
    {
      public Throwable e;

      public AGateway src;
    }
    class Starter extends Thread
    {
      AGateway gateway;

      List<ExceptionPair> eList;

      List<AGateway> gList;

      public Starter(AGateway gateway, List<ExceptionPair> eList, List<AGateway> gList)
      {
        this.gateway = gateway;
        this.eList = eList;
        this.gList = gList;
      }

      @Override
      public void run()
      {
        try
        {
          this.gateway.startGateway();
          this.gList.add(this.gateway);
        }
        catch (Exception e)
        {
          ExceptionPair exc = new ExceptionPair();
          exc.e = e;
          exc.src = this.gateway;
          this.eList.add(exc);
        }
      }
    }
    if (getSettings().CONCURRENT_GATEWAY_START)
    {
      List<ExceptionPair> eList;
      List<AGateway> gList;
      eList = new LinkedList<ExceptionPair>();
      gList = new LinkedList<AGateway>();
      if (getGateways().size() == 0) throw new SMSLibException("No gateways are defined.");
      for (AGateway gateway : getGateways())
        new Starter(gateway, eList, gList).start();
      while ((gList.size() != getGateways().size()) && (eList.size() == 0))
        Thread.sleep(1000);
      for (ExceptionPair e : eList)
        e.src.setStatus(GatewayStatuses.RESTART);
      if (startAll)
      {
        if (eList.size() != 0)
        {
          ExceptionPair e = eList.get(0);
          if (e.e instanceof TimeoutException) throw (TimeoutException) e.e;
          if (e.e instanceof GatewayException) throw (GatewayException) e.e;
          if (e.e instanceof SMSLibException) throw (SMSLibException) e.e;
          if (e.e instanceof IOException) throw (IOException) e.e;
          if (e.e instanceof InterruptedException) throw (InterruptedException) e.e;
        }
        else
        {
          eList.clear();
          gList.clear();
        }
      }
    }
    else
    {
      if (getGateways().size() == 0) throw new SMSLibException("No gateways are defined.");
      for (AGateway gateway : getGateways())
        gateway.startGateway();
    }
  }

  /**
   * Stops all gateways - does not remove them from Service's internal list.
   * Once stopped, all SMSLib operations will fail. You need to start the
   * gateways again before proceeding.
   *
   * @throws SMSLibException
   *             No Gateways are defined.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see #startService()
   */
  public synchronized void stopService() throws SMSLibException, TimeoutException, GatewayException, IOException, InterruptedException
  {
    setServiceStatus(ServiceStatus.STOPPING);
    if (getQueueManager() != null) getQueueManager().stop();
    if (getWatchDog() != null)
    {
      getWatchDog().cancel();
      setWatchDog(null);
    }
    for (AGateway gateway : getGateways())
      gateway.stopGateway();
    getNotifyQueueManager().cancel();
    setServiceStatus(ServiceStatus.STOPPED);
  }

  /**
   * Reads inbound messages from ALL gateways with the Inbound attribute set.
   * When successful, the message list will contain all messages read.
   *
   * @param msgList
   *            A (probably empty) list that will be populated with Inbound
   *            messages read.
   * @param msgClass
   *            Filtering: Class of messages that need to be read.
   * @return The number of messages read.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see MessageClasses
   */
  public int readMessages(Collection<InboundMessage> msgList, MessageClasses msgClass) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    for (AGateway gateway : getGateways())
    {
      if (gateway.isInbound())
      {
        try
        {
          readMessages(msgList, msgClass, gateway);
        }
        catch (TimeoutException e)
        {
          Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " does not respond, marking for restart.", null, null);
          gateway.setStatus(GatewayStatuses.RESTART);
        }
        catch (IOException e)
        {
          Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " throws IO errors, marking for restart.", null, null);
          gateway.setStatus(GatewayStatuses.RESTART);
        }
      }
    }
    return msgList.size();
  }

  /**
   * .NET bridge method.
   */
  public InboundMessage[] readMessages(MessageClasses msgClass) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    ArrayList<InboundMessage> messageList = new ArrayList<InboundMessage>();
    readMessages(messageList, msgClass);
    return messageList.toArray(new InboundMessage[0]);
  }

  /**
   * Reads inbound messages from the SPECIFIC gateway. When successful, the
   * message list will contain all messages read.
   *
   * @param msgList
   *            A (probably empty) list that will be populated with Inbound
   *            messages read.
   * @param msgClass
   *            Filtering: Class of messages that need to be read.
   * @param gatewayId
   *            The identifier of the gateway from which to read messages.
   * @return The number of messages read.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see MessageClasses
   * @see AGateway
   */
  public int readMessages(Collection<InboundMessage> msgList, MessageClasses msgClass, String gatewayId) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    AGateway gateway = findGateway(gatewayId);
    if ((gateway != null) && (gateway.isInbound()))
    {
      try
      {
        readMessages(msgList, msgClass, gateway);
      }
      catch (TimeoutException e)
      {
        Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " does not respond, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
      }
      catch (IOException e)
      {
        Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " throws IO errors, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
      }
    }
    return msgList.size();
  }

  /**
   * .NET bridge method.
   */
  public InboundMessage[] readMessages(MessageClasses msgClass, String gatewayId) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    ArrayList<InboundMessage> messageList = new ArrayList<InboundMessage>();
    readMessages(messageList, msgClass, gatewayId);
    return messageList.toArray(new InboundMessage[0]);
  }

  /**
   * Reads inbound messages from the SPECIFIC gateway. When successful, the
   * message list will contain all messages read.
   *
   * @param msgList
   *            A (probably empty) list that will be populated with inbound
   *            messages read.
   * @param msgClass
   *            Filtering: Class of messages that need to be read.
   * @param gateway
   *            The gateway object from which to read messages.
   * @return The number of messages read.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see MessageClasses
   * @see AGateway
   */
  public int readMessages(Collection<InboundMessage> msgList, MessageClasses msgClass, AGateway gateway) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    try
    {
      gateway.readMessages(msgList, msgClass);
    }
    catch (TimeoutException e)
    {
      Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " does not respond, marking for restart.", null, null);
      gateway.setStatus(GatewayStatuses.RESTART);
    }
    catch (IOException e)
    {
      Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " throws IO errors, marking for restart.", null, null);
      gateway.setStatus(GatewayStatuses.RESTART);
    }
    return msgList.size();
  }

  /**
   * .NET bridge method.
   */
  public InboundMessage[] readMessages(MessageClasses msgClass, AGateway gateway) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    ArrayList<InboundMessage> messageList = new ArrayList<InboundMessage>();
    readMessages(messageList, msgClass, gateway);
    return messageList.toArray(new InboundMessage[0]);
  }

  /**
   * Reads a specific gateway for a message matching the given Memory Location
   * and Memory Index.
   * <p>
   * This is a "dummy" approach. It does not implement the CGMR command,
   * rather it reads all messages and searches for a match.
   *
   * @param gatewayId
   *            The Gateway ID of the gateway to read from.
   * @param memLoc
   *            The memory location string.
   * @param memIndex
   *            The memory index.
   * @return The message read. Null if no relevant message is found or if the
   *         Gateway ID given is invalid.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public InboundMessage readMessage(String gatewayId, String memLoc, int memIndex) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return null;
    InboundMessage msg = null;
    AGateway gateway = findGateway(gatewayId);
    if ((gateway != null) && (gateway.isInbound()))
    {
      try
      {
        msg = gateway.readMessage(memLoc, memIndex);
      }
      catch (TimeoutException e)
      {
        Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " does not respond, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
      }
      catch (IOException e)
      {
        Logger.getInstance().logWarn("readMessages(): Gateway " + gateway.getGatewayId() + " throws IO errors, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
      }
    }
    return msg;
  }

  /**
   * Sends a single message. The following logic is applied in order for
   * SMSLib to decide from which gateway it will send the message:<br>
   * 1. If the message holds gateway information (member field "gatewayId"),
   * SMSLib will try to send it from that gateway.<br>
   * 2. If the message does not hold gateway information (member field
   * "gatewayId" is empty or "*") then if router and load balancer is defined,
   * then message is processed by these classes.<br>
   * 3. Otherwise the method selects the first outbound-capable gateway
   * defined and sends the message from it.<br>
   * The method blocks until the message is actually sent (synchronous
   * operation).
   *
   * @param msg
   *            An OutboundMessage object.
   * @return True if the message is sent.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see #queueMessage(OutboundMessage)
   */
  public boolean sendMessage(OutboundMessage msg) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return false;
    AGateway gateway = routeMessage(msg);
    if (gateway != null)
    {
      try
      {
        List<String> recipients = expandGroup(msg.getRecipient());
        if (recipients.size() == 0) return gateway.sendMessage(msg);
        List<OutboundMessage> groupMessages = new ArrayList<OutboundMessage>();
        for (String to : recipients)
        {
          OutboundMessage newMessage = new OutboundMessage();
          msg.copyTo(newMessage);
          newMessage.setRecipient(to);
          groupMessages.add(newMessage);
        }
        sendMessages(groupMessages);
        return true;
      }
      catch (TimeoutException e)
      {
        Logger.getInstance().logWarn("sendMessage(): Gateway " + gateway.getGatewayId() + " does not respond, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
        msg.setMessageStatus(MessageStatuses.FAILED);
        msg.setFailureCause(FailureCauses.GATEWAY_FAILURE);
        return false;
      }
      catch (IOException e)
      {
        Logger.getInstance().logWarn("sendMessage(): Gateway " + gateway.getGatewayId() + " throws IO errors, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
        msg.setMessageStatus(MessageStatuses.FAILED);
        msg.setFailureCause(FailureCauses.GATEWAY_FAILURE);
        return false;
      }
    }
    return false;
  }

  /**
   * Sends a single message from the specified gateway.
   *
   * @param msg
   *            An OutboundMessage object.
   * @param gatewayId
   *            The id of the gateway that will be used for sending.
   * @return True if the message is sent.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see #sendMessage(OutboundMessage)
   */
  public boolean sendMessage(OutboundMessage msg, String gatewayId) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return false;
    msg.setGatewayId(gatewayId);
    return sendMessage(msg);
  }

  /**
   * Sends a list of messages.
   *
   * @param msgList
   *            A list of OutboundMessage objects.
   * @return The number of messages sent.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see #sendMessage(OutboundMessage)
   */
  public int sendMessages(Collection<OutboundMessage> msgList) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    int counter = 0;
    for (OutboundMessage msg : msgList)
      if (sendMessage(msg)) counter++;
    return counter;
  }

  /**
   * .NET bridge method.
   */
  public int sendMessages(OutboundMessage[] msgArray) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    int counter = 0;
    for (int i = 0; i < msgArray.length; i++)
      if (sendMessage(msgArray[i])) counter++;
    return counter;
  }

  /**
   * Sends a list of messages from the specified gateway.
   *
   * @param msgList
   *            A list of OutboundMessage objects.
   * @param gatewayId
   *            The id of the gateway that will be used for sending.
   * @return The number of messages sent.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   * @see #sendMessage(OutboundMessage)
   */
  public int sendMessages(Collection<OutboundMessage> msgList, String gatewayId) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    int counter = 0;
    for (OutboundMessage msg : msgList)
    {
      msg.setGatewayId(gatewayId);
      if (sendMessage(msg)) counter++;
    }
    return counter;
  }

  /**
   * .NET bridge method.
   */
  public int sendMessages(OutboundMessage[] msgArray, String gatewayId) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    int counter = 0;
    for (int i = 0; i < msgArray.length; i++)
    {
      msgArray[i].setGatewayId(gatewayId);
      if (sendMessage(msgArray[i])) counter++;
    }
    return counter;
  }

  /**
   * Queues a message for sending. The gateway selection logic is the same as
   * for sendMessage(). The method does not block - returns immediately. If
   * you wish to be alerted about the fate of the message, you may implement a
   * IOutboundMessageNotification listener.
   *
   * @param msg
   *            Message to be sent
   * @return True if the message is accepted in the Queue.
   * @see #queueMessageAt(OutboundMessage, Date)
   * @see #sendMessage(OutboundMessage)
   * @see IOutboundMessageNotification
   */
  public boolean queueMessage(OutboundMessage msg)
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return false;
    List<String> recipients = expandGroup(msg.getRecipient());
    if (recipients.size() == 0)
    {
      if ("*".equals(msg.getGatewayId()))
      {
        AGateway gateway = routeMessage(msg);
        if (gateway == null) { return false; }
        msg.setGatewayId(gateway.getGatewayId());
      }
      return getQueueManager().queueMessage(msg);
    }
    for (String to : recipients)
    {
      OutboundMessage newMessage = new OutboundMessage();
      msg.copyTo(newMessage);
      newMessage.setRecipient(to);
      if ("*".equals(msg.getGatewayId()))
      {
        AGateway gateway = routeMessage(msg);
        if (gateway == null) { return false; }
        msg.setGatewayId(gateway.getGatewayId());
      }
      getQueueManager().queueMessage(newMessage);
    }
    return true;
  }

  /**
   * Queues a message for sending from the specific gateway.
   *
   * @param msg
   *            A OutboundMessage object.
   * @param gatewayId
   *            The id of the gateway that will be used for sending.
   * @return True if the message is accepted in the Queue.
   * @see #queueMessage(OutboundMessage)
   */
  public boolean queueMessage(OutboundMessage msg, String gatewayId)
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return false;
    msg.setGatewayId(gatewayId);
    return queueMessage(msg);
  }

  /**
   * Queues a list of messages for sending.
   *
   * @param msgList
   *            A list of OutboundMessage objects.
   * @return The number of messages accepted in the Queue.
   * @see #queueMessage(OutboundMessage)
   */
  public int queueMessages(Collection<OutboundMessage> msgList)
  {
    int counter = 0;
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    for (OutboundMessage msg : msgList)
      if (queueMessage(msg)) counter++;
    return counter;
  }

  /**
   * .NET bridge method.
   */
  public int queueMessages(OutboundMessage[] msgArray)
  {
    int counter = 0;
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    for (int i = 0; i < msgArray.length; i++)
      if (queueMessage(msgArray[i])) counter++;
    return counter;
  }

  /**
   * Queues a list of messages for sending from the specific gateway.
   *
   * @param msgList
   *            A list of OutboundMessage objects.
   * @param gatewayId
   *            The id of the gateway to be used for sending.
   * @return The number of messages accepted in the Queue.
   * @see #queueMessage(OutboundMessage)
   */
  public int queueMessages(Collection<OutboundMessage> msgList, String gatewayId)
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    int counter = 0;
    for (OutboundMessage msg : msgList)
    {
      msg.setGatewayId(gatewayId);
      if (queueMessage(msg)) counter++;
    }
    return counter;
  }

  /**
   * .NET bridge method.
   */
  public int queueMessages(OutboundMessage[] msgArray, String gatewayId)
  {
    int counter = 0;
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    for (int i = 0; i < msgArray.length; i++)
    {
      msgArray[i].setGatewayId(gatewayId);
      if (queueMessage(msgArray[i])) counter++;
    }
    return counter;
  }

  /**
   * Queues a message for sending at a specific time. The gateway selection
   * logic is the same as for sendMessage(). The method does not block -
   * returns immediately. If you wish to be alerted about the fate of the
   * message, you may implement a IOutboundMessageNotification listener.
   *
   * @param msg
   *            The message to be sent.
   * @param at
   *            The date/time that the message should be sent at.
   * @return True if the message is accepted in the Queue.
   * @see #queueMessageAt(OutboundMessage, long)
   * @see #queueMessage(OutboundMessage)
   */
  public boolean queueMessageAt(OutboundMessage msg, Date at)
  {
    msg.setScheduledDeliveryDate(at);
    return queueMessage(msg);
  }

  /**
   * Queues a message for sending with a specific delay. The gateway selection
   * logic is the same as for sendMessage(). The method does not block -
   * returns immediately. If you wish to be alerted about the fate of the
   * message, you may implement a IOutboundMessageNotification listener.
   *
   * @param msg
   *            The message to be sent.
   * @param delayMillis
   *            The delay (ms) that the message should be sent after.
   * @return True if the message is accepted in the Queue.
   * @see #queueMessageAt(OutboundMessage, Date)
   * @see #queueMessage(OutboundMessage)
   */
  public boolean queueMessageAt(OutboundMessage msg, long delayMillis)
  {
    msg.setDeliveryDelay(delayMillis);
    return queueMessage(msg);
  }

  /**
   * Attempts to remove the specified message from the background sending
   * queue. Can only be used for messages previously queued up with the
   * queueMessage() calls.
   *
   * @param msg
   *            The outbound message to be removed.
   * @return true if the message was removed from the queue.
   */
  public boolean removeMessage(OutboundMessage msg)
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return false;
    return getQueueManager().removePendingMessage(msg);
  }

  /**
   * Attempts to remove the message with the specified ID from the background
   * queue. Can only be used for messages previously queued with the
   * queueMessage() call.
   *
   * @param messageId
   *            The ID of the message to be removed.
   * @return true if the message was removed from the queue.
   */
  public boolean removeMessage(String messageId)
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return false;
    return getQueueManager().removePendingMessage(messageId);
  }

  /**
   * Deletes the specified message. The operation is not supported by all
   * gateways.
   *
   * @param msg
   *            The message to be deleted. It must be a valid InboundMessage
   *            object. <b>DO NOT PASS invalid objects to the method!</b>
   * @return True if the message is deleted.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public boolean deleteMessage(InboundMessage msg) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return false;
    AGateway gateway = findGateway(msg.getGatewayId());
    if (gateway != null)
    {
      try
      {
        return gateway.deleteMessage(msg);
      }
      catch (TimeoutException e)
      {
        Logger.getInstance().logWarn("deleteMessage(): Gateway " + gateway.getGatewayId() + " does not respond, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
        return false;
      }
      catch (IOException e)
      {
        Logger.getInstance().logWarn("deleteMessage(): Gateway " + gateway.getGatewayId() + " throws IO errors, marking for restart.", null, null);
        gateway.setStatus(GatewayStatuses.RESTART);
        return false;
      }
    }
    return false;
  }

  public boolean sendUSSDRequest(USSDRequest request, String gatewayId) throws GatewayException, TimeoutException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) { throw new GatewayException("Service is not started"); }
    AGateway gateway = findGateway(gatewayId);
    if (request == null) { throw new IllegalArgumentException("Cannot use a null request object"); }
    if (gateway == null) { throw new GatewayException("Cannot use a null gateway"); }
    return gateway.sendUSSDRequest(request);
  }

  /**
   * Loads the phonebook from the specified gateway into a Phonebook class.
   *
   * @param phonebook
   *            An already instantiated, empty Phonebook class.
   * @param gatewayId
   *            The gateway id for which the phonebook should be loaded
   * @return The number of phonebook entries read.
   * @throws TimeoutException
   *             The gateway did not respond in a timely manner.
   * @throws GatewayException
   *             A Gateway error occurred.
   * @throws IOException
   *             An IO error occurred.
   * @throws InterruptedException
   *             The call was interrupted.
   */
  public int readPhonebook(Phonebook phonebook, String gatewayId) throws TimeoutException, GatewayException, IOException, InterruptedException
  {
    if (getServiceStatus() != ServiceStatus.STARTED) return 0;
    AGateway gateway = findGateway(gatewayId);
    if (gateway != null) { return gateway.readPhonebook(phonebook); }
    return 0;
  }

  /**
   * Returns the total number of messages received by the specified gateway.
   *
   * @param gatewayId
   *            The id of the gateway to query.
   * @return The number of received messages or -1 on error.
   */
  public int getInboundMessageCount(String gatewayId)
  {
    return getInboundMessageCount(findGateway(gatewayId));
  }

  /**
   * Returns the total number of messages received by the specified gateway.
   *
   * @param gateway
   *            The AGateway object to query.
   * @return The number of received messages or -1 on error.
   */
  public int getInboundMessageCount(AGateway gateway)
  {
    return (gateway != null ? gateway.getInboundMessageCount() : -1);
  }

  /**
   * Returns the total number of messages sent via the specified gateway.
   *
   * @param gatewayId
   *            The id of the gateway to query.
   * @return The number of sent messages or -1 on error.
   */
  public int getOutboundMessageCount(String gatewayId)
  {
    return getOutboundMessageCount(findGateway(gatewayId));
  }

  /**
   * Returns the total number of messages sent via the specified gateway.
   *
   * @param gateway
   *            The AGateway object to query.
   * @return The number of sent messages or -1 on error.
   */
  public int getOutboundMessageCount(AGateway gateway)
  {
    return (gateway != null ? gateway.getOutboundMessageCount() : -1);
  }

  /**
   * Returns the total number of messages received by all gateways.
   *
   * @return The number of received messages.
   */
  public int getInboundMessageCount()
  {
    int total = 0;
    for (AGateway gateway : getGateways())
      total += gateway.getInboundMessageCount();
    return total;
  }

  /**
   * Returns the total number of messages sent via all gateways.
   *
   * @return The number of sent messages.
   */
  public int getOutboundMessageCount()
  {
    int total = 0;
    for (AGateway gateway : getGateways())
      total += gateway.getOutboundMessageCount();
    return total;
  }

  /**
   * Find and return a gateway by its ID.
   *
   * @param gatewayId
   *            The ID of gateway to find.
   * @return Gateway object bearing given name, or NULL if not found.
   */
  public AGateway findGateway(String gatewayId)
  {
    for (AGateway gateway : getGateways())
      if (gateway.getGatewayId().equals(gatewayId)) return gateway;
    return null;
  }

  /**
   * Returns the list of defined gateways.
   *
   * @return The list of gateways.
   */
  public Collection<AGateway> getGateways()
  {
    return this.gatewayList;
  }

  /**
   * .NET bridge method.
   */
  public AGateway[] getGatewaysNET()
  {
    return getGateways().toArray(new AGateway[0]);
  }

  /**
   * Returns the active Load Balancer class.
   *
   * @return The active LoadBalancer class.
   * @see LoadBalancer
   */
  public LoadBalancer getLoadBalancer()
  {
    return this.loadBalancer;
  }

  /**
   * Sets a new Load Balancer.
   *
   * @param loadBalancer
   *            The Load Balancer that will take effect.
   * @see LoadBalancer
   */
  public void setLoadBalancer(LoadBalancer loadBalancer)
  {
    this.loadBalancer = loadBalancer;
  }

  /**
   * Returns the active Router class.
   *
   * @return The active Router class.
   * @see Router
   */
  public Router getRouter()
  {
    return this.router;
  }

  /**
   * Sets a new Router.
   *
   * @param router
   *            The Router that will take effect.
   * @see Router
   */
  public void setRouter(Router router)
  {
    this.router = router;
  }

  /**
   * Find best suitable gateway to send specific message through Router and
   * Load Balancer.
   *
   * @param msg
   *            Message to be routed
   * @return Reference to gateway or <code>null</code> if no suitable gateway
   *         is found.
   */
  public synchronized AGateway routeMessage(OutboundMessage msg)
  {
    Collection<AGateway> gateways;
    gateways = getRouter().route(msg, getGateways());
    if (gateways.size() > 0) return getLoadBalancer().balance(msg, gateways);
    else
    {
      msg.setMessageStatus(MessageStatuses.FAILED);
      msg.setFailureCause(FailureCauses.NO_ROUTE);
      return null;
    }
  }

  /**
   * Returns the notification method set for inbound messages. Returns null if
   * no such method is set.
   *
   * @return The notification method.
   * @see #setInboundMessageNotification(IInboundMessageNotification)
   */
  public IInboundMessageNotification getInboundMessageNotification()
  {
    return this.inboundMessageNotification;
  }

  /**
   * Sets the inbound message notification method. The method must adhere to
   * the IInboundMessageNotification interface. If set, SMSLib will call this
   * method upon arrival of a new inbound message.
   *
   * @param inboundNotification
   *            The method to be called.
   * @see #getInboundMessageNotification()
   * @see IInboundMessageNotification
   */
  public void setInboundMessageNotification(IInboundMessageNotification inboundNotification)
  {
    this.inboundMessageNotification = inboundNotification;
  }

  /**
   * Returns the notification method set for outbound messages. Returns null
   * if no such method is set.
   *
   * @return The notification method.
   * @see #setOutboundMessageNotification(IOutboundMessageNotification)
   */
  public IOutboundMessageNotification getOutboundMessageNotification()
  {
    return this.outboundMessageNotification;
  }

  /**
   * Sets the outbound notification method. The method must adhere to the
   * IOutboundMessageNotification interface. If set, SMSLib will call this
   * method upon dispatch of a message through the queueing (asyncronous)
   * calls.
   *
   * @param outboundNotification
   * @see #getOutboundMessageNotification()
   * @see IOutboundMessageNotification
   */
  public void setOutboundMessageNotification(IOutboundMessageNotification outboundNotification)
  {
    this.outboundMessageNotification = outboundNotification;
  }

  /**
   * Returns the call notification method. Returns null if no such method is
   * set.
   *
   * @return The notification method.
   * @see #setCallNotification(ICallNotification)
   */
  public ICallNotification getCallNotification()
  {
    return this.callNotification;
  }

  /**
   * Sets the call notification method. The method must adhere to the
   * ICallNotification interface. If set, SMSLib will call this method upon
   * detection of an inbound call.
   *
   * @param callNotification
   * @see #getCallNotification()
   * @see ICallNotification
   */
  public void setCallNotification(ICallNotification callNotification)
  {
    this.callNotification = callNotification;
  }

  /**
   * Returns the USSD notification method. Returns null if no such method is
   * set.
   *
   * @return The USSD notification method.
   * @see #setUSSDNotification(IUSSDNotification)
   */
  public IUSSDNotification getUSSDNotification()
  {
    return this.ussdNotification;
  }

  /**
   * Sets the USSD notification method. The method must adhere to the
   * IUSSDNotification interface. If set, SMSLib will call this method upon
   * detection of an inbound USSD datagram.
   *
   * @param ussdNotification
   * @see #getUSSDNotification()
   * @see IUSSDNotification
   */
  public void setUSSDNotification(IUSSDNotification ussdNotification)
  {
    this.ussdNotification = ussdNotification;
  }

  /**
   * Returns the gateway status notification method. Returns null if no such
   * method has been set.
   *
   * @return The notification method.
   * @see #setGatewayStatusNotification(IGatewayStatusNotification)
   */
  public IGatewayStatusNotification getGatewayStatusNotification()
  {
    return this.gatewayStatusNotification;
  }

  /**
   * Sets the gateway status notification method. The method must adhere to
   * the IGatewayStatusNotification interface. If set, SMSLib will call this
   * method upon every gateway status change.
   *
   * @param gatewayStatusNotification
   * @see #getGatewayStatusNotification()
   * @see IGatewayStatusNotification
   */
  public void setGatewayStatusNotification(IGatewayStatusNotification gatewayStatusNotification)
  {
    this.gatewayStatusNotification = gatewayStatusNotification;
  }

  /**
   * Returns the notification method set for Queue sending operation. Returns
   * null if no such method is set.
   *
   * @return The notification method.
   * @see #setQueueSendingNotification(IQueueSendingNotification)
   */
  public IQueueSendingNotification getQueueSendingNotification()
  {
    return this.queueSendingNotification;
  }

  /**
   * Sets the Queue sending notification method. The method must adhere to the
   * IQueueSendingNotification interface. If set, SMSLib will call this method
   * upon dispatch of a message through the queueing (asyncronous) calls.
   *
   * @param queueSendingNotification
   * @see #getQueueSendingNotification()
   * @see IQueueSendingNotification
   */
  public void setQueueSendingNotification(IQueueSendingNotification queueSendingNotification)
  {
    this.queueSendingNotification = queueSendingNotification;
  }

  /**
   * Returns the orphaned message notification method, or null if no-one is
   * set.
   *
   * @return The orphaned notification method.
   * @see #setOrphanedMessageNotification(IOrphanedMessageNotification)
   */
  public IOrphanedMessageNotification getOrphanedMessageNotification()
  {
    return this.orphanedMessageNotification;
  }

  /**
   * Sets the orphaned message notification. The method must implement the
   * IOrphanedMessageNotification interface.
   *
   * @param orphanedMessageNotification
   * @see #getOrphanedMessageNotification()
   */
  public void setOrphanedMessageNotification(IOrphanedMessageNotification orphanedMessageNotification)
  {
    this.orphanedMessageNotification = orphanedMessageNotification;
  }

  public long getStartMillis()
  {
    return this.startMillis;
  }

  void setSettings(Settings settings)
  {
    this.S = settings;
  }

  public ServiceStatus getServiceStatus()
  {
    return this.serviceStatus;
  }

  void setServiceStatus(ServiceStatus myServiceStatus)
  {
    this.serviceStatus = myServiceStatus;
  }

  /**
   * Returns the Settings object, holding SMSLib run-time values.
   *
   * @return The Settings object.
   * @see Settings
   */
  public Settings getSettings()
  {
    return this.S;
  }

  /**
   * Creates a destination group. A group can hold an unlimited number of
   * recipients. Sending a message to a predefined group expands and sends the
   * message to all numbers defined by the group. A group is valid for as long
   * as this instance of SMSLib is active.
   *
   * @param groupName
   *            The group name.
   * @return True if the group creation succeded.
   * @see #removeGroup(String)
   */
  public boolean createGroup(String groupName)
  {
    this.groups.add(new Group(groupName));
    return true;
  }

  /**
   * Removes a group.
   *
   * @param groupName
   *            The name of the group to be removed.
   * @return True if the removal was a success.
   * @see #createGroup(String)
   */
  public boolean removeGroup(String groupName)
  {
    for (Group a : this.groups)
    {
      if (a.getName().equalsIgnoreCase(groupName))
      {
        a.clear();
        this.groups.remove(a);
        return true;
      }
    }
    return false;
  }

  /**
   * Expands a group to its recipient numbers.
   *
   * @param groupName
   *            The group name to be expanded.
   * @return A list of the numbers that this group represents. If the group is
   *         not defined, an empty list is returned.
   * @see #addToGroup(String, String)
   * @see #removeFromGroup(String, String)
   */
  public ArrayList<String> expandGroup(String groupName)
  {
    for (Group a : this.groups)
    {
      if (a.getName().equalsIgnoreCase(groupName)) { return new ArrayList<String>(a.getNumbers()); }
    }
    return new ArrayList<String>();
  }

  /**
   * Adds a number to the specified group.
   *
   * @param groupName
   *            The group to which the number is to be added.
   * @param number
   *            The number to add.
   * @return True if the number is added. False if the group is not found.
   * @see #createGroup(String)
   * @see #removeFromGroup(String, String)
   */
  public boolean addToGroup(String groupName, String number)
  {
    for (Group a : this.groups)
    {
      if (a.getName().equalsIgnoreCase(groupName))
      {
        a.addNumber(number);
        return true;
      }
    }
    return false;
  }

  /**
   * Removes a number from the specified group.
   *
   * @param groupName
   *            The group from which the number is to be removed.
   * @param number
   *            The number to remove.
   * @return True if the number was removed. False if the group or the number
   *         is not found.
   * @see #removeGroup(String)
   * @see #addToGroup(String, String)
   */
  public boolean removeFromGroup(String groupName, String number)
  {
    for (Group a : this.groups)
    {
      if (a.getName().equalsIgnoreCase(groupName)) { return a.removeNumber(number); }
    }
    return false;
  }

  /**
   * Checks if a number belongs to a group.
   *
   * @param groupName
   *            The group in which the number is to be searched.
   * @param number
   *            The number to search.
   * @return True if the number is found.
   */
  public boolean checkInGroup(String groupName, String number)
  {
    for (Group a : this.groups)
    {
      if (a.getName().equalsIgnoreCase(groupName))
      {
        Collection<String> numbers = a.getNumbers();
        return numbers.contains(number);
      }
    }
    return false;
  }

  public boolean setQueueManager(AbstractQueueManager myQueueManager)
  {
    if (getServiceStatus() != ServiceStatus.STOPPED) return false;
    this.queueManager = myQueueManager;
    return true;
  }

  public AbstractQueueManager getQueueManager()
  {
    return this.queueManager;
  }

  void setNotifyQueueManager(NotifyQueueManager myNotifyQueueManager)
  {
    this.notifyQueueManager = myNotifyQueueManager;
  }

  public NotifyQueueManager getNotifyQueueManager()
  {
    return this.notifyQueueManager;
  }

  public KeyManager getKeyManager()
  {
    return this.keyManager;
  }

  WatchDog getWatchDog()
  {
    return this.watchDog;
  }

  void setWatchDog(WatchDog myWatchDog)
  {
    this.watchDog = myWatchDog;
  }

  private class WatchDog extends AServiceThread
  {
    public WatchDog(String name, int delay)
    {
      super(name, delay, 0, true);
    }

    @Override
    public void process() throws Exception
    {
      if (getServiceStatus() != ServiceStatus.STARTED) return;
      for (AGateway gateway : getGateways())
      {
        if (gateway.getStatus() == GatewayStatuses.RESTART)
        {
          Logger.getInstance().logWarn("Gateway: " + gateway.getGatewayId() + " restarting.", null, null);
          try
          {
            gateway.stopGateway();
            gateway.setStatus(GatewayStatuses.RESTART);
          }
          catch (Exception e)
          {
            gateway.setStatus(GatewayStatuses.RESTART);
            Logger.getInstance().logWarn("Error while shutting down Gateway: " + gateway.getGatewayId(), e, null);
          }
          try
          {
            gateway.startGateway();
          }
          catch (Exception e)
          {
            gateway.setStatus(GatewayStatuses.RESTART);
            Logger.getInstance().logError("Error while starting Gateway: " + gateway.getGatewayId(), e, null);
          }
        }
      }
    }
  }

  public static void main(String[] args)
  {
    System.out.println(Library.getLibraryDescription());
    System.out.println("\nSMSLib API Version: " + Library.getLibraryVersion());
  }
}
TOP

Related Classes of org.smslib.Service

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.