/**
* $Id: TopicConfigManager.java 1831 2013-05-16 01:39:51Z shijia.wxr $
*/
package com.alibaba.rocketmq.broker.topic;
import static com.alibaba.rocketmq.common.protocol.route.ObjectConverter.props2TopicConfigTable;
import io.netty.channel.ChannelHandlerContext;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.rocketmq.broker.BrokerController;
import com.alibaba.rocketmq.common.DataVersion;
import com.alibaba.rocketmq.common.MixAll;
import com.alibaba.rocketmq.common.TopicConfig;
import com.alibaba.rocketmq.store.schedule.ScheduleMessageService;
/**
* @author vintage.wang@gmail.com shijia.wxr@taobao.com
* @author lansheng.zj@taobao.com
*
*/
public class TopicConfigManager {
private static final Logger log = LoggerFactory.getLogger(MixAll.BrokerLoggerName);
// Topic����
private final ConcurrentHashMap<String, TopicConfig> topicConfigTable =
new ConcurrentHashMap<String, TopicConfig>(1024);
private final Lock lockTopicConfigTable = new ReentrantLock();
private static final long LockTimeoutMillis = 3000;
private final DataVersion dataVersion = new DataVersion();
private final BrokerController brokerController;
public TopicConfigManager(BrokerController brokerController) {
this.brokerController = brokerController;
// MixAll.DEFAULT_TOPIC
TopicConfig topicConfig = new TopicConfig(MixAll.DEFAULT_TOPIC);
topicConfig.setReadQueueNums(this.brokerController.getBrokerConfig().getDefaultTopicQueueNums());
topicConfig.setWriteQueueNums(this.brokerController.getBrokerConfig().getDefaultTopicQueueNums());
int perm = this.brokerController.getBrokerConfig().isAutoCreateTopic() ? MixAll.PERM_INHERIT : 0;
perm |= MixAll.PERM_READ | MixAll.PERM_WRITE;
topicConfig.setPerm(perm);
this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
// MixAll.SELF_TEST_TOPIC
topicConfig = new TopicConfig(MixAll.SELF_TEST_TOPIC);
topicConfig.setReadQueueNums(1);
topicConfig.setWriteQueueNums(1);
this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
// ��Ⱥ����
topicConfig = new TopicConfig(this.brokerController.getBrokerConfig().getBrokerClusterName());
perm = MixAll.PERM_INHERIT;
if (this.brokerController.getBrokerConfig().isClusterTopicEnable()) {
perm |= MixAll.PERM_READ | MixAll.PERM_WRITE;
}
topicConfig.setPerm(perm);
this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
}
public boolean load() {
try {
String fileName = this.brokerController.getBrokerConfig().getTopicConfigPath();
String content = MixAll.file2String(fileName);
if (content != null) {
Properties prop = MixAll.string2Properties(content);
if (prop != null) {
return this.decode(prop);
}
}
}
catch (Exception e) {
}
return true;
}
public String getCurrentDataVersion() {
return this.dataVersion.currentVersion();
}
public String encodeIncludeSysTopic() {
return this.encode(true);
}
public String encodeNotIncludeSysTopic() {
return this.encode(false);
}
private String encode(boolean includeSysTopic) {
if (!this.topicConfigTable.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (TopicConfig config : this.topicConfigTable.values()) {
if (!includeSysTopic) {
if (this.isSystemTopic(config.getTopicName()))
continue;
}
sb.append(config.getTopicName() + "=" + config.encode() + IOUtils.LINE_SEPARATOR);
}
return sb.toString();
}
return null;
}
private boolean decode(final Properties prop) {
topicConfigTable.putAll(props2TopicConfigTable(prop, log));
return true;
}
private boolean isSystemTopic(final String topic) {
boolean res = //
topic.equals(MixAll.DEFAULT_TOPIC)//
|| topic.equals(MixAll.SELF_TEST_TOPIC)//
|| topic.equals(this.brokerController.getBrokerConfig().getBrokerClusterName())//
|| topic.equals(ScheduleMessageService.SCHEDULE_TOPIC)//
|| topic.equals(MixAll.SELF_TEST_TOPIC);
return res;
}
public boolean isTopicCanSendMessage(final String topic) {
boolean reservedWords =
topic.equals(MixAll.DEFAULT_TOPIC)
|| topic.equals(this.brokerController.getBrokerConfig().getBrokerClusterName());
return !reservedWords;
}
public synchronized void flush() {
String content = this.encodeNotIncludeSysTopic();
if (content != null) {
String fileName = this.brokerController.getBrokerConfig().getTopicConfigPath();
boolean result = MixAll.string2File(content, fileName);
log.info("flush topic config, " + fileName + (result ? " OK" : " Failed"));
}
this.dataVersion.nextVersion();
}
public TopicConfig selectTopicConfig(final String topic) {
return this.topicConfigTable.get(topic);
}
/**
* ����Ϣʱ�����Topic�����ڣ����Դ���
*/
public TopicConfig createTopicInSendMessageMethod(final String topic, final String defaultTopic,
final ChannelHandlerContext ctx, final int clientDefaultTopicQueueNums) {
final String remoteAddress = ctx != null ? ctx.channel().remoteAddress().toString() : "UNKNOW ADDR";
try {
if (this.lockTopicConfigTable.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
TopicConfig topicConfig = this.topicConfigTable.get(topic);
if (topicConfig != null)
return topicConfig;
TopicConfig defaultTopicConfig = this.topicConfigTable.get(defaultTopic);
if (defaultTopicConfig != null) {
if (MixAll.isInherited(defaultTopicConfig.getPerm())) {
topicConfig = new TopicConfig(topic);
int queueNums =
clientDefaultTopicQueueNums > defaultTopicConfig.getWriteQueueNums() ? defaultTopicConfig
.getWriteQueueNums() : clientDefaultTopicQueueNums;
if (queueNums < 0) {
queueNums = 0;
}
topicConfig.setReadQueueNums(queueNums);
topicConfig.setWriteQueueNums(queueNums);
int perm = defaultTopicConfig.getPerm();
perm &= ~MixAll.PERM_INHERIT;
topicConfig.setPerm(perm);
topicConfig.setTopicFilterType(defaultTopicConfig.getTopicFilterType());
}
else {
log.warn("create new topic failed, because the default topic[" + defaultTopic
+ "] no perm, " + defaultTopicConfig.getPerm() + " producer: " + remoteAddress);
}
}
else {
log.warn("create new topic failed, because the default topic[" + defaultTopic + "] not exist."
+ " producer: " + remoteAddress);
}
if (topicConfig != null) {
log.info("create new topic by default topic[" + defaultTopic + "], " + topicConfig
+ " producer: " + remoteAddress);
this.topicConfigTable.put(topic, topicConfig);
this.flush();
}
return topicConfig;
}
}
catch (InterruptedException e) {
log.error("createTopicInSendMessageMethod exception", e);
}
return null;
}
public void updateTopicConfig(final TopicConfig topicConfig) {
TopicConfig old = this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
if (old != null) {
log.info("update topic config, old: " + old + " new: " + topicConfig);
}
else {
log.info("create new topic, " + topicConfig);
}
this.flush();
}
public void deleteTopicConfig(final String topic) {
TopicConfig old = this.topicConfigTable.remove(topic);
if (old != null) {
log.info("delete topic config OK, topic: " + old);
this.flush();
}
else {
log.warn("delete topic config failed, topic: " + topic + " not exist");
}
}
}