package esmska.transfer; import esmska.data.Links; import esmska.data.Queue.Events; import esmska.data.event.ValuedEvent; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingWorker; import esmska.data.Queue; import esmska.data.SMS; import esmska.data.Tuple; import esmska.utils.L10N; import esmska.data.event.ValuedListener; import esmska.transfer.GatewayExecutor.Problem; import java.text.MessageFormat; import java.util.List; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** Sender of SMS * * @author ripper */ public class SMSSender { private static final Logger logger = Logger.getLogger(SMSSender.class.getName()); private static final ResourceBundle l10n = L10N.l10nBundle; private static final Queue queue = Queue.getInstance(); private static final String SENDING_CRASHED_ERROR = MessageFormat.format(l10n.getString("SMSSender.SENDING_CRASHED_ERROR"), Links.ISSUES); /** map of <gateway,worker>; it shows whether some gateway has currently assigned a background worker (therefore is sending at the moment) */ private HashMap<String,SMSWorker> workers = new HashMap<String, SMSWorker>(); /** Custom executor for executing SwingWorkers. Needed because of bug in Java 6u18: * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6880336 * http://forums.sun.com/thread.jspa?threadID=5424356 */ private static final ExecutorService executor = Executors.newCachedThreadPool(); /** Creates a new instance of SMSSender */ public SMSSender() { queue.addValuedListener(new QueueListener()); } /** Return whether there is currently some message being sent. * @return true is some message is just being sent; false otherwise */ public boolean isRunning() { return !workers.isEmpty(); } /** Send new ready SMS * @param gatewayName gateway for which to look for new ready sms; * use null for any gateway */ private void sendNew(String gatewayName) { if (queue.isPaused()) { //don't send anything while queue is paused return; } List<SMS> readySMS = queue.getAllWithStatus(SMS.Status.READY, gatewayName); for (SMS sms : readySMS) { String gateway = sms.getGateway(); if (workers.containsKey(gateway)) { //there's already some message from this gateway being sent, //skip this message continue; } logger.log(Level.FINE, "Sending new SMS: {0}", sms.toDebugString()); queue.setSMSSending(sms); SMSWorker worker = new SMSWorker(sms); workers.put(gateway, worker); //send in worker thread executor.execute(worker); } } /** Handle processed SMS */ private void finishedSending(SMS sms, boolean success) { logger.log(Level.FINE, "Finished sending SMS: {0}", sms.toDebugString()); workers.remove(sms.getGateway()); if (success) { queue.setSMSSent(sms); } else { if (sms.getProblem() == null) { logger.log(Level.SEVERE, "SMS sending failed, but no problem is set: {0}", sms); sms.setProblem(new Tuple<Problem, String>(Problem.UNKNOWN, null)); } queue.setSMSFailed(sms); } //look for another sms to send sendNew(sms.getGateway()); } /** send sms over internet */ private class SMSWorker extends SwingWorker<Boolean, Void> { private SMS sms; public SMSWorker(SMS sms) { super(); this.sms = sms; } @Override protected void done() { boolean success = false; try { success = get(); } catch (Throwable t) { logger.log(Level.SEVERE, "Sending of SMS crashed", t); sms.setProblem(new Tuple<Problem, String>( Problem.INTERNAL_MESSAGE, SENDING_CRASHED_ERROR)); } finishedSending(sms, success); } @Override protected Boolean doInBackground() { boolean success = false; try { GatewayInterpreter interpreter = new GatewayInterpreter(); sms.setProblem(null); sms.setSupplMsg(null); success = interpreter.sendMessage(sms); } catch (Exception ex) { logger.log(Level.WARNING, "Error while sending sms", ex); success = false; if (sms.getProblem() == null) { sms.setProblem(new Tuple<Problem, String>(Problem.UNKNOWN, null)); } } return success; } } /** Listen for changes in the sms queue */ private class QueueListener implements ValuedListener<Queue.Events, SMS> { @Override public void eventOccured(ValuedEvent<Events, SMS> e) { switch (e.getEvent()) { //on new sms ready try to send it case NEW_SMS_READY: case QUEUE_RESUMED: sendNew(null); break; } } } }