Message Schedules

Alternative Admin Stream Message Scheduler (since 10.2.0)

Please have a look at the new Message Scheduler that is part of the Admin Streams package since release 10.2.0. It is much more scalable!

Overview

Any JMS client can use the Scheduler Swiftlet to schedule a message job. For this the Scheduler Swiftlet listens on a queue "swiftmqscheduler" of the local router. To schedule such a message job, a JMS client creates a JMS message of whatever type, fills it with content and sets some JMS-vendor properties to define the schedule. The Scheduler Swiftlet accepts the message, verifies it and automatically creates a schedule of a "Message Sender" job which is provided from the Scheduler Swiftlet itself. This job sends the very same message to the specified destination on the schedule, specified via the JMS properties in the message.

The schedule of this message job will be stored in an internal queue named "sys$scheduler" on the local router. This queue is maintained by the Scheduler Swiftlet. If the message is persistent, it will survive reboots and the schedule is activated during the startup of the router.

When the schedule expires, it will be automatically deleted. It is also possible to delete the schedule programmatically from a JMS client. This is done via a schedule-id which is returned as a receipt after a successful creation. A message schedule can also be deleted administratively.

Whether a JMS client is allowed to schedule message jobs is dependent whether it has a send grant on the queue "swiftmqscheduler" of the local router.

JMS Vendor Properties

The following table specifies the JMS vendor properties to use from JMS clients to add or remove a message schedule:

Property Type Mandatory Description
JMS_SWIFTMQ_SCHEDULER_COMMAND String Yes Either "add" to create a schedule or "remove" to delete a schedule.
JMS_SWIFTMQ_SCHEDULER_ID String Depends Required for "remove" and must contain the id of the schedule.
JMS_SWIFTMQ_SCHEDULER_DESTINATION String Yes The name of the JMS destination (queue name or topic name).
JMS_SWIFTMQ_SCHEDULER_DESTINATION_TYPE String Yes The type of the destination, either "queue" or "topic".
JMS_SWIFTMQ_SCHEDULER_CALENDAR String No The name of the calendar to use.
JMS_SWIFTMQ_SCHEDULER_TIME_EXPRESSION String Yes The time expression (either an 'at' or a 'repeat' expression).
JMS_SWIFTMQ_SCHEDULER_DATE_FROM String Yes The date when the schedule should start. Format is yyyy-MM-dd or 'now'.
JMS_SWIFTMQ_SCHEDULER_DATE_TO String Yes The date after which the schedule expires. Format is yyyy-MM-dd or 'forever'.
JMS_SWIFTMQ_SCHEDULER_ENABLE_LOGGING Boolean Yes Whether start/stop of the message jobs should be logged to the router's log file. Not recommended for short intervals.
JMS_SWIFTMQ_SCHEDULER_MAY_EXPIRE_WHILE_ROUTER_DOWN Boolean No For 'at' schedules only. If set to true (default), a schedule may expire while a router is shut down. If set to false and the last time in the 'at' expresion has already been reached (schedule expired), the job is executed anyway once 1 second after startup and then removed. This is useful to ensure that message jobs are executed in any case even so if the router was shut down and they are expired after restart. Since 8.0.0.
JMS_SWIFTMQ_SCHEDULER_EXPIRATION Long No The final message expiration. This value is set on each message sent as the JMSExpiration.
JMS_SWIFTMQ_SCHEDULER_ERROR Boolean -- This property is set in a receipt message to indicate an error.
JMS_SWIFTMQ_SCHEDULER_ERROR_TEXT String -- This property is set in a receipt message if "JMS_SWIFTMQ_SCHEDULER_ERROR" returns true. It contains the error text.

Creating a Schedule without a Receipt

Creating a schedule without a receipt is not recommended, except you are absolutely sure that everything you have specified is ok.

First of all you have to create a queue connection, a session, and a sender to queue "swiftmqscheduler" of the router you want to create the schedule (you can use any router since the Scheduler Swiftlet is part of the SwiftMQ kernel):

Example:

  Hashtable env = new Hashtable();
  env.put(Context.INITIAL_CONTEXT_FACTORY, "com.swiftmq.jndi.InitialContextFactoryImpl");
  env.put(Context.PROVIDER_URL, "smqp://localhost:4001/timeout=10000");
  InitialContext ctx = new InitialContext(env);
  QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup("plainsocket@router1");
  Queue queue = (Queue) ctx.lookup("swiftmqscheduler@router1");
  ctx.close();

  QueueConnection qc = qcf.createQueueConnection();
  qc.start();
  QueueSession session = qc.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
  QueueSender sender = session.createSender(queue);

Next is to create the message to be scheduled and set the JMS vendor properties to define the schedule:

Example:

  TextMessage msg = session.createTextMessage();
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_COMMAND","add");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DESTINATION","testqueue@router1");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DESTINATION_TYPE","queue");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_CALENDAR","Business Days");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DATE_FROM","2003-02-17");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DATE_TO","forever");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_TIME_EXPRESSION","start 09:00 stop 18:00 delay 10s");
  msg.setBooleanProperty("JMS_SWIFTMQ_SCHEDULER_ENABLE_LOGGING",false);
  msg.setLongProperty("JMS_SWIFTMQ_SCHEDULER_EXPIRATION",60000);
  msg.setText("This is a scheduled message!");

Following is another example that schedules a message to be delivered once 2 hours later from now. This is a good practice to delay the delivery of a message if it can't be processed yet.

Example:

  // Calculate from/to date and time expression (2h later)
  SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd");
  SimpleDateFormat timeFmt = new SimpleDateFormat("'at' HH:mm:ss");
  Calendar cal = Calendar.getInstance();
  cal.add(Calendar.HOUR_OF_DAY,2);
  String date = dateFmt.format(cal.getTime());
  String timeExpr = timeFmt.format(cal.getTime());

  // Create the schedule
  TextMessage msg = session.createTextMessage();
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_COMMAND","add");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DESTINATION","testqueue@router1");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DESTINATION_TYPE","queue");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_CALENDAR","Business Days");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DATE_FROM",date);
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DATE_TO",date);
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_TIME_EXPRESSION",timeExpr);
  msg.setBooleanProperty("JMS_SWIFTMQ_SCHEDULER_ENABLE_LOGGING",false);
  msg.setLongProperty("JMS_SWIFTMQ_SCHEDULER_EXPIRATION",60000);
  msg.setText("This is a scheduled message!");

Send the message and you're done:

Example:

  sender.send(msg);
  qc.close();

Creating a Schedule with a Receipt

The Scheduler Swiftlet optionally sends a receipt of the message schedule. To get a receipt, the JMS client must set the JMSReplyTo accordingly and then receive the receipt from that destination:

Example:

  // Create a temp queue for the receipt
  TemporaryQueue tq = session.createTemporaryQueue();

  // Create the schedule
  TextMessage msg = session.createTextMessage();
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_COMMAND","add");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DESTINATION","testqueue@router1");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DESTINATION_TYPE","queue");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_CALENDAR","Business Days");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DATE_FROM","2003-02-17");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_DATE_TO","forever");
  msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_TIME_EXPRESSION","start 09:00 stop 18:00 delay 10s");
  msg.setBooleanProperty("JMS_SWIFTMQ_SCHEDULER_ENABLE_LOGGING",false);
  msg.setLongProperty("JMS_SWIFTMQ_SCHEDULER_EXPIRATION",60000);
  msg.setText("This is a scheduled message!");

  // Set the JMSReplyTo for the receipt
  msg.setJMSReplyTo(tq);

  // Send the schedule
  sender.send(msg);

The Scheduler Swiftlet returns the very same message as a receipt. The message contains the property JMS_SWIFTMQ_SCHEDULER_ERROR to indicate an error. If that property is true, the property JMS_SWIFTMQ_SCHEDULER_ERROR_TEXT contains an error text. If it is false, the property JMS_SWIFTMQ_SCHEDULER_ID contains the id under which the schedule is stored. It can be used to remove the schedule programmatically (see next section):

Example:

  QueueReceiver receiver = session.createReceiver(tq);
  Message receipt = receiver.receive();
  boolean error = receipt.getBooleanProperty("JMS_SWIFTMQ_SCHEDULER_ERROR");
  if (!error)
    System.out.println("ID: "+receipt.getStringProperty("JMS_SWIFTMQ_SCHEDULER_ID"));
  else
    System.out.println("Error: "+receipt.getStringProperty("JMS_SWIFTMQ_SCHEDULER_ERROR_TEXT"));
  qc.close();

Removing a Schedule programmatically

To remove a schedule programmatically, the JMS client must know the schedule id which is returned with the receipt. It then sends a message with the JMS vendor properties JMS_SWIFTMQ_SCHEDULER_COMMAND ("remove") and JMS_SWIFTMQ_SCHEDULER_ID to the queue "swiftmqscheduler" at the resp. router to remove the schedule. It can optionally request a receipt by setting the JMSReplyTo. See the following example:

Example:

  public static void main(String[] args)
  {
    if (args.length != 4)
    {
      System.out.println("usage: java DelSchedule <smqp-url> <qcf> <queue> <id>");
      System.exit(-1);
    }
    try
    {
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.swiftmq.jndi.InitialContextFactoryImpl");
      env.put(Context.PROVIDER_URL, args[0]);
      InitialContext ctx = new InitialContext(env);
      QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup(args[1]);
      Queue queue = (Queue) ctx.lookup(args[2]);
      ctx.close();

      QueueConnection qc = qcf.createQueueConnection();
      qc.start();
      QueueSession session = qc.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
      QueueSender sender = session.createSender(queue);
      TemporaryQueue tq = session.createTemporaryQueue();

      Message msg = session.createMessage();
      msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_COMMAND","remove");
      msg.setStringProperty("JMS_SWIFTMQ_SCHEDULER_ID",args[3]);
      msg.setJMSReplyTo(tq);
      sender.send(msg);

      QueueReceiver receiver = session.createReceiver(tq);
      QueueReceiver receiver = session.createReceiver(tq);
      Message receipt = receiver.receive();
      boolean error = receipt.getBooleanProperty("JMS_SWIFTMQ_SCHEDULER_ERROR");
      if (error)
        System.out.println("Error: "+receipt.getStringProperty("JMS_SWIFTMQ_SCHEDULER_ERROR_TEXT"));
      qc.close();

    } catch (Exception e)
    {
      System.err.println("Exception: " + e);
      System.exit(-1);
    }
  }

Removing a Schedule administratively

Message schedules are listed in folder "Active Message Schedules" below the "Usage" folder of the Scheduler Swiftlet:

Select the one you like to remove, right mouse click, and select "Delete Entity":

After you have confirmed the deletion, the schedule will be removed.