/* * QueueTemplate.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 2006-2008 Sun Microsystems, Inc. * * Sun Public License Notice * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is included as the file "license.terms", * and also available at http://www.sun.com/ * * The Original Code is from: * Brazil project web application toolkit release 2.3. * The Initial Developer of the Original Code is: suhler. * Portions created by suhler are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): suhler. * * Version: 1.11 * Created by suhler on 06/01/27 * Last modified by suhler on 08/09/11 09:57:14 * * Version Histories: * * 1.11 08/09/11-09:57:14 (suhler) * change "lastGet" to match documentation (e.g. last attempted get) * * 1.10 08/06/24-11:56:48 (suhler) * small doc fix * . * * 1.9 08/06/24-11:33:57 (suhler) * Add max=nn to queueifo to set max queue length * . * * 1.8 08/03/04-10:57:02 (suhler) * javadoc fixes * * 1.7 06/12/10-10:12:38 (suhler) * document "glob" attribute, add "delim" * . * . * * 1.6 06/11/13-15:40:28 (suhler) * - internal shuffling in preparation for queue aging * * 1.5 06/08/04-09:35:59 (suhler) * add an option to getQ to not create the Q if it doesn't already exist * * 1.4 06/06/15-09:54:15 (suhler) * doc fixes * * 1.3 06/06/14-15:45:58 (suhler) * add enough information to a queue so applications may destroy it when it * is no longer in use. This is intended to help the AsteriskTemplate * decide when a client has gone away without unregistering for events. * This allows long runnig applications that use Queue to dispose of unused * ones in an application specific way * * 1.2 06/01/27-08:36:01 (suhler) * Version from the Asterisk package * * 1.2 06/01/27-08:27:18 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 1 0 handlers/templates/QueueTemplate.java * * 1.1 06/01/27-08:27:17 (suhler) * date and time created 06/01/27 08:27:17 by suhler * */ package sunlabs.brazil.template; import java.util.Vector; import java.util.StringTokenizer; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import sunlabs.brazil.session.SessionManager; import sunlabs.brazil.util.Glob; import sunlabs.brazil.server.Server; import sunlabs.brazil.util.StringMap; import sunlabs.brazil.template.RewriteContext; import sunlabs.brazil.template.Template; /* * New version of Q template. * Instead of "message" and "meta", we will xfer a "properties" table * if name/value pairs. This should be backward compatible */ /** * Template class for Managing simple Queues, allowing text communication * among sessions. * The "name" attribute names the Q. When enqueueing messages, "name" * is a white space separated list of queue recipients. When Dequeueing * Messages, "name" is the recipient Queue. *
* The following tags are recognized. *
<enqueue name="recipients ..." data=var1 var2 ... varn"
* [glob="..." delim="." from="sender" nocreate="true|false"] >
* *
<dequeue name="name" prepend="props prefix"
* timelimit="sec">
* If "timelimit" has a suffix of "ms", then the time is taken in ms.
* <queueinfo name="q_name" prepend="props prefix" clear
remove=true|false create=true|false max=n>
* <enqueue name="recipients ..." meta="anything" message="msg"
* from="sender" [nocreate="true|false"] >
* delim
* list of queues to send the data too.* The property "count" contains the number of recipients for which * the message was successfully Queued. The property "error.name" * will contain an error message if Queueing failed. In both cases, * the template prefix will be prepended. It is not considered an * error condition for a message not to be delivered to a non existent * Queue if "nocreate" is set. *
* The experimental attribute "glob" may be used instead of "name",
* in which case name is taken as the list of "delim" separated
* tokens named by the first wildcard substring of the matching
* glob pattern.
*/
public void
tag_enqueue(RewriteContext hr) {
String names = hr.get("name");
String glob = hr.get("glob"); // specify names as a glob pattern in properties
String from = hr.get("from", "anonymous");
String delim = hr.get("delim", " ");
boolean noCreate = hr.isTrue("nocreate");
boolean force = hr.isTrue("force");
String vars=hr.get("data");
// these are obsolete, and have been replaced by "data"
String meta = hr.get("meta");
String message = hr.get("message", "Hello");
hr.killToken();
debug(hr);
/* supply names as a glob pattern - experimental */
if (names==null && glob != null) {
StringBuffer sb = new StringBuffer();
Enumeration e = hr.request.props.propertyNames();
String[] substr = new String[1];
boolean ok = false;
while(e.hasMoreElements()) {
if (Glob.match(glob, (String) e.nextElement(), substr)) {
sb.append(ok?delim:"").append(substr[0]);
ok=true;
}
}
if (ok) {
names = sb.toString();
hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix,
"enqueue " + glob + "=(" + names + ")");
}
}
if (names==null) {
debug(hr, "Missing Q name");
return;
}
// gather dictionary of values to put into Q. We'll use a
// StringMap as we don't expect that many items. We could
// do wildcard matching. Missing values get "".
StringMap data = new StringMap();
if (vars != null) {
StringTokenizer st = new StringTokenizer(vars);
while (st.hasMoreTokens()) {
String name = st.nextToken();
String dflt = "";
int idx = name.indexOf("#");
if (idx > 0) {
dflt = name.substring(idx+1);
name=name.substring(0,idx);
}
data.put(name, hr.request.props.getProperty(name, dflt));
}
} else { // backward compatible
data.put("message", message);
if (meta != null) {
data.put("meta", meta);
}
}
QueueItem item = new QueueItem(from, data);
// set to the queue or queues
StringTokenizer st = new StringTokenizer(names, delim);
int count = 0;
while (st.hasMoreTokens()) {
String name = st.nextToken();
Queue q = getQ(name, !noCreate);
if (q != null && !q.put(item, force)) {
hr.request.props.put(hr.prefix + "error." + name, " Q full");
} else {
count++;
}
}
hr.request.props.put(hr.prefix + "count", "" + count);
}
/**
* Allow a message to be enqueued from java code.
* Use the {@link #enqueue(String to, String from, Dictionary data,
* boolean noCreate, boolean force)} method instead.
*
* @deprecated
* @param to The queue name
* @param from The sender of the data
* @param message The message to enqueue
* @param meta The meta data, if any
* @param noCreate If true, don't create a Q if it doesn't already exist
* @param force Force item onto Q even if it is closed
* @return True, if the data was enqueued
*/
public static boolean
enqueue(String to, String from, String message,
String meta, boolean noCreate, boolean force) {
StringMap data = new StringMap();
data.put("message", message);
data.put("meta", meta);
QueueItem item = new QueueItem(from, data);
return enqueue(to, from, data, noCreate, force);
}
/**
* Allow a message to be enqueued from java code.
* @param to The queue name (only a single q)
* @param from The sender of the data
* @param data a dictionary f name/value pairs
* @param noCreate If true, don't create a Q if it doesn'a already exist
* @param force Force item onto Q even if it is closed
* @return True, if the data was enqueued
*/
public static boolean
enqueue(String to, String from, Dictionary data,
boolean noCreate, boolean force) {
QueueItem item = new QueueItem(from, data);
Queue q = getQ(to, !noCreate);
// for backward compatibility
item.message = (String) data.get("message");
item.meta = (String) data.get("meta");
return (q != null && q.put(item, force));
}
/**
* Remove a Queue, freeing its resources.
*/
public static void
destroyQueue(String name) {
SessionManager.remove(name, Q_ID);
// queueTable.remove(name);
}
/**
* Remove an item from the queue, and generate the appropriate properties.
* Attributes:
*
* Note: this tag blocks until an item is received in the Queue
* (or a timelimit expires). As template processing is synchronized
* based on sessions, care should be taken to avoid blocking
* other (unrelated) session based requests while waiting on
* the queue.
*/
public void
tag_dequeue(RewriteContext hr) {
String name = hr.get("name");
String prepend = hr.get("prepend", hr.prefix);
String timelimit = hr.get("timelimit", "30");
if (!prepend.endsWith(".")) {
prepend += ".";
}
int timer = 30;
int scale=1000;
if (timelimit.endsWith("ms")) {
timelimit = timelimit.substring(0,timelimit.length()-2);
scale=1;
}
try {
timer = Integer.decode(timelimit).intValue();
} catch (Exception e) {}
hr.killToken();
debug(hr);
if (name == null) {
debug(hr, "Missing Q name");
return;
}
Queue q = getQ(name);
hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix,
name + ": Q.get(" + timer + ")...");
QueueItem item = (QueueItem) q.get(timer*scale);
if (item != null) {
hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix,
name + ": Q.get(" + timer + ") " + item);
// dump message into properties
Enumeration keys = item.data.keys();
while(keys.hasMoreElements()) {
String key = (String) keys.nextElement();
hr.request.props.put(prepend + key, item.data.get(key));
}
// now put meta-data into properties, overriding message
// data if needed (?)
hr.request.props.put(prepend + "from", item.from);
hr.request.props.put(prepend + "age", "" +
(System.currentTimeMillis() - item.timestamp)/1000);
hr.request.props.put(prepend + "sent", "" + (item.timestamp/1000));
} else {
hr.request.log(Server.LOG_DIAGNOSTIC, hr.prefix,
name + ": Q.get() Timeout");
hr.request.props.put(prepend + "error", "timelimit");
}
return;
}
/**
* Program access to the Q.
* @param name The name of the Q. A new Q will be created if
* it doesn't already exist.
* @param timelimit how long (in ms) to wait before returning
* @return The Queue item, or Null if no item is available.
*/
public static QueueItem
dequeue(String name, int timelimit) {
Queue q = getQ(name);
return (QueueItem) q.get(timelimit*1000);
}
/**
* Return info about the Q, and optionally clear or remove it.
* If the queue doesn't already exist, it is created.
* Attributes:
*