/**
* $Id: DefaultMQProducerImpl.java 1831 2013-05-16 01:39:51Z shijia.wxr $
*/
package com.alibaba.rocketmq.client.impl.producer;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.rocketmq.client.QueryResult;
import com.alibaba.rocketmq.client.exception.MQBrokerException;
import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.client.impl.CommunicationMode;
import com.alibaba.rocketmq.client.impl.MQClientManager;
import com.alibaba.rocketmq.client.impl.factory.MQClientFactory;
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
import com.alibaba.rocketmq.client.producer.LocalTransactionExecuter;
import com.alibaba.rocketmq.client.producer.LocalTransactionState;
import com.alibaba.rocketmq.client.producer.MessageQueueSelector;
import com.alibaba.rocketmq.client.producer.SendCallback;
import com.alibaba.rocketmq.client.producer.SendResult;
import com.alibaba.rocketmq.client.producer.SendStatus;
import com.alibaba.rocketmq.common.Message;
import com.alibaba.rocketmq.common.MessageDecoder;
import com.alibaba.rocketmq.common.MessageExt;
import com.alibaba.rocketmq.common.MessageId;
import com.alibaba.rocketmq.common.MessageQueue;
import com.alibaba.rocketmq.common.MixAll;
import com.alibaba.rocketmq.common.UtilALl;
import com.alibaba.rocketmq.common.ServiceState;
import com.alibaba.rocketmq.common.TopicFilterType;
import com.alibaba.rocketmq.common.protocol.MQProtos.MQResponseCode;
import com.alibaba.rocketmq.common.protocol.header.EndTransactionRequestHeader;
import com.alibaba.rocketmq.common.protocol.header.SendMessageRequestHeader;
import com.alibaba.rocketmq.common.sysflag.MessageSysFlag;
import com.alibaba.rocketmq.remoting.common.RemotingHelper;
import com.alibaba.rocketmq.remoting.common.RemotingUtil;
import com.alibaba.rocketmq.remoting.exception.RemotingException;
import com.alibaba.rocketmq.remoting.protocol.RemotingProtos.ResponseCode;
/**
* ������Ĭ��ʵ��
*
* @author vintage.wang@gmail.com shijia.wxr@taobao.com
*/
public class DefaultMQProducerImpl {
private ServiceState serviceState = ServiceState.CREATE_JUST;
private final DefaultMQProducer defaultMQProducer;
private final ConcurrentHashMap<String/* topic */, TopicPublishInfo> topicPublishInfoTable =
new ConcurrentHashMap<String, TopicPublishInfo>();
private MQClientFactory mQClientFactory;
public DefaultMQProducerImpl(final DefaultMQProducer defaultMQProducer) {
this.defaultMQProducer = defaultMQProducer;
}
private void checkConfig() throws MQClientException {
if (null == this.defaultMQProducer.getProducerGroup()) {
throw new MQClientException("producerGroup is null", null);
}
if (this.defaultMQProducer.getProducerGroup().equals(MixAll.DEFAULT_PRODUCER_GROUP)) {
throw new MQClientException("producerGroup can not equal " + MixAll.DEFAULT_PRODUCER_GROUP
+ ", please specify another one.", null);
}
}
public void start() throws MQClientException {
switch (this.serviceState) {
case CREATE_JUST:
this.checkConfig();
this.serviceState = ServiceState.RUNNING;
this.mQClientFactory =
MQClientManager.getInstance().getAndCreateMQClientFactory(
this.defaultMQProducer.getMQClientConfig());
boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);
if (!registerOK) {
this.serviceState = ServiceState.CREATE_JUST;
throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup()
+ "] has created already, specifed another name please.", null);
}
// Ĭ��Topicע��
this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());
mQClientFactory.start();
break;
case RUNNING:
break;
case SHUTDOWN_ALREADY:
break;
default:
break;
}
}
public void shutdown() {
switch (this.serviceState) {
case CREATE_JUST:
break;
case RUNNING:
this.serviceState = ServiceState.SHUTDOWN_ALREADY;
this.mQClientFactory.unregisterProducer(this.defaultMQProducer.getProducerGroup());
this.mQClientFactory.shutdown();
break;
case SHUTDOWN_ALREADY:
break;
default:
break;
}
}
private void makeSureStateOK() throws MQClientException {
if (this.serviceState != ServiceState.RUNNING) {
throw new MQClientException("The producer service state not OK", null);
}
}
public void updateTopicPublishInfo(final String topic, final TopicPublishInfo info) {
if (info != null && topic != null) {
TopicPublishInfo prev = this.topicPublishInfoTable.put(topic, info);
if (prev != null) {
// TODO log
}
}
}
public Set<String> getPublishTopicList() {
Set<String> topicList = new HashSet<String>();
for (String key : this.topicPublishInfoTable.keySet()) {
topicList.add(key);
}
return topicList;
}
public void createTopic(String key, String newTopic, int queueNum, TopicFilterType topicFilterType,
boolean order) throws MQClientException {
this.makeSureStateOK();
this.mQClientFactory.getMQAdminImpl().createTopic(key, newTopic, queueNum, topicFilterType, order);
}
public List<MessageQueue> fetchPublishMessageQueues(String topic) throws MQClientException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().fetchPublishMessageQueues(topic);
}
public long searchOffset(MessageQueue mq, long timestamp) throws MQClientException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().searchOffset(mq, timestamp);
}
public long getMaxOffset(MessageQueue mq) throws MQClientException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().getMaxOffset(mq);
}
public long getMinOffset(MessageQueue mq) throws MQClientException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().getMinOffset(mq);
}
public long getEarliestMsgStoreTime(MessageQueue mq) throws MQClientException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().getEarliestMsgStoreTime(mq);
}
public MessageExt viewMessage(String msgId) throws RemotingException, MQBrokerException, InterruptedException,
MQClientException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().viewMessage(msgId);
}
public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end)
throws MQClientException, InterruptedException {
this.makeSureStateOK();
return this.mQClientFactory.getMQAdminImpl().queryMessage(topic, key, maxNum, begin, end);
}
private void checkMessage(Message msg) throws MQClientException {
// topic TODO
// tags TODO
// keys TODO
// body TODO
if (null == msg.getBody()) {
throw new MQClientException("the message body is null", null);
}
if (msg.getBody().length > this.defaultMQProducer.getMaxMessageSize()) {
throw new MQClientException("the message body size over max value, MAX: "
+ this.defaultMQProducer.getMaxMessageSize(), null);
}
}
private boolean tryToCompressMessage(final Message msg) {
byte[] body = msg.getBody();
if (body != null) {
if (body.length >= this.defaultMQProducer.getCompressMsgBodyOverHowmuch()) {
try {
byte[] data = UtilALl.compress(body, 9);
if (data != null) {
msg.setBody(data);
return true;
}
}
catch (IOException e) {
// TODO log
}
}
}
return false;
}
private SendResult sendDefaultImpl(//
Message msg,//
final CommunicationMode communicationMode,//
final SendCallback sendCallback//
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
final long beginTimestamp = System.currentTimeMillis();
long endTimestamp = beginTimestamp;
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
if (topicPublishInfo != null && topicPublishInfo.ok()) {
MessageQueue mq = null;
Exception exception = null;
SendResult sendResult = null;
for (int times = 0; times < 3
&& (endTimestamp - beginTimestamp) < this.defaultMQProducer.getSendMsgTimeout(); times++) {
String lastBrokerName = null == mq ? null : mq.getBrokerName();
mq = topicPublishInfo.selectOneMessageQueue(lastBrokerName);
if (mq != null) {
try {
sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback);
endTimestamp = System.currentTimeMillis();
switch (communicationMode) {
case ASYNC:
return null;
case ONEWAY:
return null;
case SYNC:
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {
continue;
}
}
return sendResult;
default:
break;
}
}
catch (RemotingException e) {
exception = e;
endTimestamp = System.currentTimeMillis();
continue;
}
catch (MQClientException e) {
exception = e;
endTimestamp = System.currentTimeMillis();
continue;
}
catch (MQBrokerException e) {
exception = e;
endTimestamp = System.currentTimeMillis();
switch (e.getResponseCode()) {
case MQResponseCode.TOPIC_NOT_EXIST_VALUE:
case MQResponseCode.SERVICE_NOT_AVAILABLE_VALUE:
case ResponseCode.SYSTEM_ERROR_VALUE:
case MQResponseCode.NO_PERMISSION_VALUE:
continue;
default:
if (sendResult != null) {
return sendResult;
}
throw e;
}
}
catch (InterruptedException e) {
throw e;
}
}
else {
break;
}
} // end of for
if (sendResult != null) {
return sendResult;
}
throw new MQClientException("Retry many times, still failed", exception);
}
throw new MQClientException("No route info of this topic, " + msg.getTopic(), null);
}
private SendResult sendKernelImpl(final Message msg,//
final MessageQueue mq,//
final CommunicationMode communicationMode,//
final SendCallback sendCallback//
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
String brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName());
if (null == brokerAddr) {
// TODO �˴����ܶ�Name Serverѹ��������Ҫ����
this.mQClientFactory.updateTopicRouteInfoFromNameServer(mq.getTopic());
this.mQClientFactory.updateTopicRouteInfoFromNameServer(this.defaultMQProducer.getCreateTopicKey());
brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName());
}
if (brokerAddr != null) {
byte[] prevBody = msg.getBody();
try {
int sysFlag = 0;
if (this.tryToCompressMessage(msg)) {
sysFlag |= MessageSysFlag.CompressedFlag;
}
final String tranMsg = msg.getProperty(Message.PROPERTY_TRANSACTION_PREPARED);
if (tranMsg != null && Boolean.parseBoolean(tranMsg)) {
sysFlag |= MessageSysFlag.TransactionPreparedType;
}
SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());
requestHeader.setTopic(msg.getTopic());
requestHeader.setDefaultTopic(this.defaultMQProducer.getCreateTopicKey());
requestHeader.setDefaultTopicQueueNums(this.defaultMQProducer.getDefaultTopicQueueNums());
requestHeader.setQueueId(mq.getQueueId());
requestHeader.setSysFlag(sysFlag);
requestHeader.setBornTimestamp(System.currentTimeMillis());
requestHeader.setFlag(msg.getFlag());
requestHeader.setProperties(MessageDecoder.messageProperties2String(msg.getProperties()));
SendResult sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(//
brokerAddr,// 1
mq.getBrokerName(),// 2
msg,// 3
requestHeader,// 4
this.defaultMQProducer.getSendMsgTimeout(),// 5
communicationMode,// 6
sendCallback// 7
);
return sendResult;
}
finally {
msg.setBody(prevBody);
}
}
throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null);
}
private SendResult sendSelectImpl(//
Message msg,//
MessageQueueSelector selector,//
Object arg,//
final CommunicationMode communicationMode,//
final SendCallback sendCallback//
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
if (topicPublishInfo != null && topicPublishInfo.ok()) {
MessageQueue mq = null;
try {
mq = selector.select(topicPublishInfo.getMessageQueueList(), msg, arg);
}
catch (Throwable e) {
throw new MQClientException("select message queue throwed exception.", e);
}
if (mq != null) {
return this.sendKernelImpl(msg, mq, communicationMode, sendCallback);
}
else {
throw new MQClientException("select message queue return null.", null);
}
}
throw new MQClientException("No route info for this topic, " + msg.getTopic(), null);
}
/**
* ����Ѱ��Topic·����Ϣ�����û����Name Server���ң���û�У���ȡĬ��Topic
*/
private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {
TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
if (null == topicPublishInfo) {
this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
this.mQClientFactory.updateTopicRouteInfoFromNameServer(this.defaultMQProducer.getCreateTopicKey());
topicPublishInfo = this.topicPublishInfoTable.get(topic);
}
if (topicPublishInfo != null && topicPublishInfo.ok()) {
return topicPublishInfo;
}
return this.topicPublishInfoTable.get(this.defaultMQProducer.getCreateTopicKey());
}
/**
* DEFAULT SYNC -------------------------------------------------------
*/
public SendResult send(Message msg) throws MQClientException, RemotingException, MQBrokerException,
InterruptedException {
this.makeSureStateOK();
this.checkMessage(msg);
return this.sendDefaultImpl(msg, CommunicationMode.SYNC, null);
}
/**
* DEFAULT ASYNC -------------------------------------------------------
*/
public void send(Message msg, SendCallback sendCallback) throws MQClientException, RemotingException,
InterruptedException {
this.makeSureStateOK();
this.checkMessage(msg);
try {
this.sendDefaultImpl(msg, CommunicationMode.ASYNC, sendCallback);
}
catch (MQBrokerException e) {
throw new MQClientException("unknow exception", e);
}
}
/**
* DEFAULT ONEWAY -------------------------------------------------------
*/
public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {
this.makeSureStateOK();
this.checkMessage(msg);
try {
this.sendDefaultImpl(msg, CommunicationMode.ONEWAY, null);
}
catch (MQBrokerException e) {
throw new MQClientException("unknow exception", e);
}
}
/**
* KERNEL SYNC -------------------------------------------------------
*/
public SendResult send(Message msg, MessageQueue mq) throws MQClientException, RemotingException,
MQBrokerException, InterruptedException {
this.makeSureStateOK();
this.checkMessage(msg);
return this.sendKernelImpl(msg, mq, CommunicationMode.SYNC, null);
}
/**
* KERNEL ASYNC -------------------------------------------------------
*/
public void send(Message msg, MessageQueue mq, SendCallback sendCallback) throws MQClientException,
RemotingException, InterruptedException {
this.makeSureStateOK();
this.checkMessage(msg);
try {
this.sendKernelImpl(msg, mq, CommunicationMode.ASYNC, sendCallback);
}
catch (MQBrokerException e) {
throw new MQClientException("unknow exception", e);
}
}
/**
* KERNEL ONEWAY -------------------------------------------------------
*/
public void sendOneway(Message msg, MessageQueue mq) throws MQClientException, RemotingException,
InterruptedException {
this.makeSureStateOK();
this.checkMessage(msg);
try {
this.sendKernelImpl(msg, mq, CommunicationMode.ONEWAY, null);
}
catch (MQBrokerException e) {
throw new MQClientException("unknow exception", e);
}
}
/**
* SELECT SYNC -------------------------------------------------------
*/
public SendResult send(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException,
RemotingException, MQBrokerException, InterruptedException {
return this.sendSelectImpl(msg, selector, arg, CommunicationMode.SYNC, null);
}
/**
* SELECT ASYNC -------------------------------------------------------
*/
public void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback)
throws MQClientException, RemotingException, InterruptedException {
try {
this.sendSelectImpl(msg, selector, arg, CommunicationMode.ASYNC, sendCallback);
}
catch (MQBrokerException e) {
throw new MQClientException("unknow exception", e);
}
}
/**
* SELECT ONEWAY -------------------------------------------------------
*/
public void sendOneway(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException,
RemotingException, InterruptedException {
try {
this.sendSelectImpl(msg, selector, arg, CommunicationMode.ONEWAY, null);
}
catch (MQBrokerException e) {
throw new MQClientException("unknow exception", e);
}
}
private void endTransaction(final SendResult sendResult, final LocalTransactionState localTransactionState)
throws RemotingException, MQBrokerException, InterruptedException, UnknownHostException {
final MessageId id = MessageDecoder.decodeMessageId(sendResult.getMsgId());
final String addr = RemotingUtil.socketAddress2String(id.getAddress());
EndTransactionRequestHeader requestHeader = new EndTransactionRequestHeader();
requestHeader.setCommitLogOffset(id.getOffset());
switch (localTransactionState) {
case COMMIT_MESSAGE:
requestHeader.setCommitOrRollback(MessageSysFlag.TransactionCommitType);
break;
case ROLLBACK_MESSAGE:
requestHeader.setCommitOrRollback(MessageSysFlag.TransactionRollbackType);
break;
case UNKNOW:
break;
default:
break;
}
requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());
requestHeader.setTranStateTableOffset(sendResult.getQueueOffset());
this.mQClientFactory.getMQClientAPIImpl().endTransaction(addr, requestHeader,
this.defaultMQProducer.getSendMsgTimeout());
}
public SendResult sendMessageInTransaction(final Message msg, final LocalTransactionExecuter tranExecuter)
throws MQClientException {
if (null == msg) {
throw new MQClientException("msg is null", null);
}
if (null == tranExecuter) {
throw new MQClientException("tranExecuter is null", null);
}
// ��һ������Broker����һ��Prepared��Ϣ
SendResult sendResult = null;
msg.putProperty(Message.PROPERTY_TRANSACTION_PREPARED, "true");
msg.putProperty(Message.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup());
try {
sendResult = this.send(msg);
}
catch (Exception e) {
throw new MQClientException("send message Exception", e);
}
// �ڶ������ص���������
LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;
MQClientException exception = null;
try {
localTransactionState = tranExecuter.executeLocalTransactionBranch(msg);
if (null == localTransactionState) {
localTransactionState = LocalTransactionState.UNKNOW;
}
}
catch (Throwable e) {
exception =
new MQClientException("send message OK, but execute local transaction branch Exception", e);
}
// ���������ύ���ع�Broker����Ϣ
switch (localTransactionState) {
case COMMIT_MESSAGE:
case ROLLBACK_MESSAGE:
try {
this.endTransaction(sendResult, localTransactionState);
}
catch (Exception e) {
throw new MQClientException("local transaction execute " + localTransactionState
+ ", but end broker transaction failed", e);
}
break;
// ���������׳��쳣���߷���δ֪״̬��������ʱ���ύҲ���ع����ȴ�����������check
case UNKNOW:
// TODO log
default:
break;
}
return sendResult;
}
}