package com.thenetcircle.comsumerdispatcher.job; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.QueueingConsumer; import com.thenetcircle.comsumerdispatcher.Bootstrap; import com.thenetcircle.comsumerdispatcher.config.DispatcherJob; import com.thenetcircle.comsumerdispatcher.util.FileUtil; import com.thenetcircle.comsumerdispatcher.util.HttpUtil; public class JobExecutor extends DispatcherJob implements Runnable, Cloneable { private static Log _logger = LogFactory.getLog(JobExecutor.class); private ReentrantLock runLock; volatile AtomicInteger completedJobs; private AtomicBoolean logErrorJobToFile; private long DELIVERY_WAIT_TIMEOUT = 3000; public void run() { _logger.info("started: " + this.getName() + " with params: " + super.toString()); ConnectionFactory factory = new ConnectionFactory(); factory.setUsername(this.getFetcherQConf().getUserName()); factory.setPassword(this.getFetcherQConf().getPassword()); factory.setVirtualHost(this.getFetcherQConf().getVhost()); factory.setHost(this.getFetcherQConf().getHost()); factory.setPort(this.getFetcherQConf().getPort()); factory.setAutomaticRecoveryEnabled(true); factory.setNetworkRecoveryInterval(5000); //factory.setRequestedHeartbeat(); Connection conn = null; Channel channel = null; try { conn = factory.newConnection(); // in one thread channel = conn.createChannel(); String exchangeName = getExchange();//"image_admin_exchange"; String type = this.getType(); String queueName = getQueue();//"image_admin"; boolean exclusive = false; boolean autoDelete = false; boolean durable = true; String routingKey = ""; channel.exchangeDeclare(exchangeName, type, durable); channel.queueDeclare(queueName, durable, exclusive, autoDelete, null); channel.queueBind(queueName, exchangeName, routingKey); boolean autoAck = false; QueueingConsumer consumer = new QueueingConsumer(channel); channel.basicQos(getPrefetchCount()); channel.basicConsume(queueName, autoAck, consumer); //channel.basicQos(getPrefetchCount()); boolean run = true; while (run) { final ReentrantLock runLock = this.runLock; runLock.lock(); try { QueueingConsumer.Delivery delivery; try { delivery = consumer.nextDelivery(DELIVERY_WAIT_TIMEOUT); if(null == delivery) continue; } catch (InterruptedException ie) { _logger.error("[THREAD INTERRUPT] consumer get interrupted :" + queueName); run = false; continue; } byte[] bobyByte = delivery.getBody(); String bodyStr = new String(bobyByte, this.getEncoding());//"US-ASCII", "utf-8" if(dispatchJob(this.getName(), bodyStr)) { channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); completedJobs.incrementAndGet(); _logger.info("ack meg:" + delivery.getEnvelope().getDeliveryTag()); } else { channel.basicReject(delivery.getEnvelope().getDeliveryTag(), true); } if (Bootstrap.once) run = false; } finally { runLock.unlock(); } } } catch (IOException e) { _logger.error(e, e); } finally { try { if (channel != null) channel.close(); } catch (IOException e) { _logger.error(e, e); } try { if (conn!= null) conn.close(); } catch (IOException e) { _logger.error(e, e); } } } private boolean dispatchJob(String qname, String body) { String vhost = this.getFetcherQConf().getHost() + "@" + this.getFetcherQConf().getVhost() ; try { Map<String, String> map = new HashMap<String, String>(); map.put("queueName", qname); map.put("bodyData", body); String result = HttpUtil.sendHttpPost(this.getUrl(), this.getUrlhost(), map, this.getTimeout()); if(null != result) result = result.trim(); if ("ok".equalsIgnoreCase(result)) { _logger.info("the result of job for q " + qname + " on server " + vhost + ":" + result); return true; } else { if(_logger.isErrorEnabled()) _logger.error("the result of job part is not right for q " + qname + " on server " + vhost); if(_logger.isDebugEnabled()) _logger.debug("the result of job part is not right for q " + qname + " on server " + vhost + ": " + ", body: " + body + ", response: " + result); if(logErrorJobToFile.get()) { // get logged to file, then acknowledge this job to queue FileUtil.logJobRawDataToFile(FileUtil.getErrorJobFileName(this), body); return true; } if(getRetry() == 0) { // disable retry _logger.info("get error, but wont retry for q " + qname + " on server " + vhost + ": " + ", body: " + body + ", response: " + result); return true; } return false; } } catch (Exception e) { if(_logger.isErrorEnabled()) _logger.error("the status of job part is not right for q " + qname + " on server " + vhost + ": " + e.getMessage()); if(logErrorJobToFile.get()) { // get logged to file, then acknowledge this job to queue FileUtil.logJobRawDataToFile(FileUtil.getErrorJobFileName(this), body); return true; } return false; } } public void setRunLock(ReentrantLock runLock) { this.runLock = runLock; } public ReentrantLock getRunLock() { return this.runLock; } public AtomicInteger getCompletedJobs() { return completedJobs; } public void setCompletedJobs(AtomicInteger completedJobs) { this.completedJobs = completedJobs; } public void setLogErrorJobToFile(AtomicBoolean logErrorJobToFile) { this.logErrorJobToFile = logErrorJobToFile; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }