package com.trendrr.beanstalk; import java.io.ByteArrayOutputStream; import java.util.Date; import java.util.logging.Logger; /** * @author dustin norlander */ public class BeanstalkClient { protected Logger log = Logger.getLogger("com.trendrr.beanstalk"); protected BeanstalkConnection con; protected String addr; protected int port; protected String tube; boolean reap = false; Date inUseSince = null; Date lastUsed = null; BeanstalkPool pool = null; private boolean inited = false; public BeanstalkClient(BeanstalkConnection con) { this.con = con; this.inited = true; } public BeanstalkClient(String addr, int port) { this(addr, port, null); } public BeanstalkClient(String addr, int port, String tube) { this.addr = addr; this.port = port; this.tube = tube; } public BeanstalkClient(String addr, int port, String tube, BeanstalkPool pool) { this.addr = addr; this.port = port; this.tube = tube; this.pool = pool; } private void init() throws BeanstalkException { if (this.inited && this.con.isOpen()) return; this.inited = true; this.con = new BeanstalkConnection(); this.con.connect(this.addr, this.port); if (this.tube != null) { this.useTube(this.tube); this.watchTube(this.tube); this.ignoreTube("default"); } } /** * Buries a job ("buried" state means the job will not be touched by the server again until "kicked"). * * @param job The job to bury. This job must previously have been reserved. * @param priority The new priority to assign to the job. * @throws BeanstalkException If an unexpected response is received from the server, or other unexpected * problem occurs. */ public void bury(BeanstalkJob job, int priority) throws BeanstalkException { try { this.init(); String command = "bury " + job.getId() + " " + priority + "\r\n"; log.finer(command); con.write(command); String line = con.readControlResponse(); log.finer(line); if (!line.startsWith("BURIED")) throw new BeanstalkException(line); } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } catch (BeanstalkException x) { throw x; } catch (Exception x) { throw new BeanstalkException(x); } } /** * will return the connection to the pool, or close the underlying socket if this * did not come from a pool */ public void close() { if (this.pool == null) { if (this.con != null) this.con.close(); return; } pool.done(this); } public void deleteJob(BeanstalkJob job) throws BeanstalkException { deleteJob(job.getId()); } public void deleteJob(long id) throws BeanstalkException { try { this.init(); String command = "delete " + id + "\r\n"; log.finer(command); con.write(command); String line = con.readControlResponse(); log.finer(line); if (line.startsWith("DELETED")) return; throw new BeanstalkException(line); } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } } public void ignoreTube(String tube) throws BeanstalkException { try { this.init(); con.write("ignore " + tube + "\r\n"); String line = con.readControlResponse(); log.finer(line); if (line.startsWith("WATCHING")) return; throw new BeanstalkException(line); } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } } /** * Puts a task into the currently used queue (see {@link #useTube(String)}. * * @param priority The job priority, from 0 to 2^32. Most urgent = 0, least urgent = 4294967295. * @param delay The time the server will wait before putting the job on the ready queue. * @param ttr The job time-to-run. The server will automatically release the job after this TTR (in seconds) * after a client reserves it. * @param data The job data. * @return The id of the inserted job. * @throws BeanstalkException If an unexpected response is received from the server, or other unexpected * problem occurs. */ public long put(long priority, int delay, int ttr, byte[] data) throws BeanstalkException { try { this.init(); String command = "put " + priority + " " + delay + " " + ttr + " " + data.length + "\r\n"; ByteArrayOutputStream buf = new ByteArrayOutputStream(); buf.write(command.getBytes()); buf.write(data); buf.write("\r\n".getBytes()); con.write(buf.toByteArray()); String line = con.readControlResponse(); if (line.startsWith("INSERTED")) return Long.parseLong(line.replaceAll("[^0-9]", "")); throw new BeanstalkException(line); } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } catch (BeanstalkException x) { throw x; } catch (Exception x) { throw new BeanstalkException(x); } } public void release(long id, int priority, int delay) throws BeanstalkException { try { this.init(); String command = "release " + id + " " + priority + " " + delay + "\r\n"; log.finer(command); con.write(command); String line = con.readControlResponse(); log.finer(line); if (!line.startsWith("RELEASED")) throw new BeanstalkException(line); } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } catch (BeanstalkException x) { throw x; } catch (Exception x) { throw new BeanstalkException(x); } } /** * Releases a job (places it back onto the queue). * * @param job The job to release. This job must previously have been reserved. * @param priority The new priority to assign to the released job. * @param delay The number of seconds the server should wait before placing the job onto the ready queue. * @throws BeanstalkException If an unexpected response is received from the server, or other unexpected * problem occurs. */ public void release(BeanstalkJob job, int priority, int delay) throws BeanstalkException { release(job.getId(), priority, delay); } /** * Reserves a job from the queue. * * @param timeoutSeconds The number of seconds to wait for a job. Null if a job should be reserved * only if immediately available. * @return The head of the queue, or null if the specified timeout elapses before a job is available. * @throws BeanstalkException If an unexpected response is received from the server, or other unexpected * problem occurs. */ public BeanstalkJob reserve(Integer timeoutSeconds) throws BeanstalkException { try { this.init(); String command = "reserve\r\n"; if (timeoutSeconds != null) command = "reserve-with-timeout " + timeoutSeconds + "\r\n"; log.finer(command); con.write(command); String line = con.readControlResponse(); log.finer(line); if (line.startsWith("TIMED_OUT")) return null; if (!line.startsWith("RESERVED")) throw new BeanstalkException(line); String[] tmp = line.split("\\s+"); long id = Long.parseLong(tmp[1]); int numBytes = Integer.parseInt(tmp[2]); log.finer("ID : " + id); log.finer("numbytes: " + numBytes); byte[] bytes = con.readBytes(numBytes); BeanstalkJob job = new BeanstalkJob(); job.setData(bytes); job.setId(id); job.setClient(this); return job; } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } catch (BeanstalkException x) { throw x; } catch (Exception x) { throw new BeanstalkException(x); } } /** * stats for the current tube * * @throws BeanstalkException */ public String tubeStats() throws BeanstalkException { return this.tubeStats(this.tube); } public String tubeStats(String tube) throws BeanstalkException { try { this.init(); String command = "stats-tube " + tube + "\r\n"; con.write(command); String line = con.readControlResponse(); if (!line.startsWith("OK")) throw new BeanstalkException(line); int numBytes = Integer.parseInt(line.split(" ")[1]); String response = new String(con.readBytes(numBytes)); log.info(response); return response; } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } } public void useTube(String tube) throws BeanstalkException { try { this.init(); con.write("use " + tube + "\r\n"); String line = con.readControlResponse(); log.finer(line); if (line.startsWith("USING")) return; throw new BeanstalkException(line); } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } } public void watchTube(String tube) throws BeanstalkException { try { this.init(); con.write("watch " + tube + "\r\n"); String line = con.readControlResponse(); log.finer(line); if (line.startsWith("WATCHING")) return; throw new BeanstalkException(line); } catch (BeanstalkDisconnectedException x) { this.reap = true; throw x; } } }