package com.trendmicro.tme.broker;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.protobuf.TextFormat;
import com.sun.messaging.jmq.jmsclient.runtime.BrokerInstance;
import com.sun.messaging.jmq.jmsclient.runtime.ClientRuntime;
import com.trendmicro.codi.CODIException;
import com.trendmicro.codi.DataListener;
import com.trendmicro.codi.DataObserver;
import com.trendmicro.codi.ZKSessionManager;
import com.trendmicro.codi.ZNode;
import com.trendmicro.codi.lock.ZLock;
import com.trendmicro.codi.lock.Lock.LockType;
import com.trendmicro.mist.proto.ZooKeeperInfo;
public class EmbeddedOpenMQ implements DataListener {
private final static Logger logger = LoggerFactory.getLogger(EmbeddedOpenMQ.class);
private BrokerInstance brokerInstance;
private Properties config;
private String host;
private boolean statusChanged = false;
private boolean isOnline = false;
private DataObserver obs;
private String mqHome;
private String mqVar;
private String mqLib;
private String tmeRoot;
public EmbeddedOpenMQ(String mqHome, String mqVar, String mqLib, Properties config) throws Exception {
host = InetAddress.getLocalHost().getHostAddress();
this.mqHome = mqHome;
this.mqVar = mqVar;
this.mqLib = mqLib;
this.config = config;
tmeRoot = config.getProperty("com.trendmicro.tme.broker.zookeeper.tmeroot");
obs = new DataObserver(tmeRoot + "/broker/" + host, this, false, 0);
}
public void start() throws Exception {
logger.info("Starting OpenMQ Broker...");
ClientRuntime clientRuntime = ClientRuntime.getRuntime();
brokerInstance = clientRuntime.createBrokerInstance();
// Although only -imqhome and -varhome options are listed as possible
// args, as shown in BrokerInstance.parseArgs(), the -libhome option is
// also supported according to BrokerProcess.convertArgs(), where the
// BrokerProcess class is the base class of all three possible
// JMSBroker implementations.
//
// See: com.sun.messaging.jmq.jmsclient.runtime.BrokerInstance
// com.sun.messaging.jmq.jmsclient.runtime.impl.BrokerInstanceImpl
// com.sun.messaging.jmq.jmsserver.BrokerProcess.convertArgs()
Properties brokerConfig = brokerInstance.parseArgs(new String[] {
"-imqhome", mqHome, "-varhome", mqVar, "-libhome", mqLib
});
brokerConfig.putAll(config);
brokerInstance.init(brokerConfig, null);
brokerInstance.start();
logger.info("OpenMQ Broker started!");
isOnline = true;
}
public void stop() {
logger.info("Stopping OpenMQ Broker...");
brokerInstance.stop();
logger.info("Shutting down OpenMQ Broker...");
brokerInstance.shutdown();
logger.info("OpenMQ Broker is down.");
isOnline = false;
}
public void registerOnZk() throws CODIException, UnknownHostException {
ZooKeeperInfo.Broker.Builder brkBuilder = ZooKeeperInfo.Broker.newBuilder();
brkBuilder.setHost(host);
brkBuilder.setPort(config.getProperty("imq.portmapper.port"));
brkBuilder.setStatus(ZooKeeperInfo.Broker.Status.ONLINE);
brkBuilder.addAccount(ZooKeeperInfo.Broker.Account.newBuilder().setUser("admin").setPassword("admin").build());
brkBuilder.setBrokerType("openmq");
brkBuilder.setVersion("4.5.1");
brkBuilder.setReserved(false);
ZNode brkNode = new ZNode(tmeRoot + "/broker/" + host);
try {
brkNode.deleteRecursively();
}
catch(Exception e) {
}
brkNode.create(false, brkBuilder.build().toString());
ZNode loadingNode = new ZNode(tmeRoot + "/broker/" + host + "/loading");
loadingNode.create(false, ZooKeeperInfo.Loading.newBuilder().setLoading(0).setLastUpdate(0).setFreeMemory(0).setMaxMemory(0).build().toString());
obs.start();
}
public void unregisterOnZk() throws CODIException {
obs.stop();
ZNode brkNode = new ZNode(tmeRoot + "/broker/" + host);
brkNode.deleteRecursively();
}
private ZooKeeperInfo.Broker.Status getNewStatus() {
try {
byte[] data = new ZNode(tmeRoot + "/broker/" + host).getContent();
ZooKeeperInfo.Broker.Builder builder = ZooKeeperInfo.Broker.newBuilder();
TextFormat.merge(new String(data), builder);
return builder.build().getStatus();
}
catch(Exception e) {
logger.error("getNewStatus() error", e);
return null;
}
}
private synchronized boolean checkOnlineStatus() {
if(statusChanged) {
String lockPath = tmeRoot + "/locks/brk_" + host;
ZLock lock = new ZLock(lockPath);
try {
logger.info("Waiting for broker lock...");
lock.acquire(LockType.WRITE_LOCK);
logger.info("Get broker lock.");
ZooKeeperInfo.Broker.Status newStatus = getNewStatus();
if((newStatus == ZooKeeperInfo.Broker.Status.ONLINE) != isOnline) {
switch(newStatus) {
case OFFLINE:
stop();
break;
case ONLINE:
start();
break;
default:
break;
}
}
}
catch(Exception e) {
logger.error(e.getMessage(), e);
}
finally {
try {
lock.release();
if(lock.getChildren().isEmpty())
lock.delete();
}
catch(CODIException e) {
}
}
statusChanged = false;
}
return isOnline;
}
public void reportLoadingForever() throws InterruptedException {
ZNode loadingNode = new ZNode(tmeRoot + "/broker/" + host + "/loading");
while(true) {
if(checkOnlineStatus()) {
long free = Runtime.getRuntime().freeMemory();
long total = Runtime.getRuntime().totalMemory();
long max = Runtime.getRuntime().maxMemory();
ZooKeeperInfo.Loading.Builder load_builder = ZooKeeperInfo.Loading.newBuilder();
load_builder.setLoading(Math.round(((float) (max - free) / max) * 100));
load_builder.setLastUpdate(new Date().getTime());
load_builder.setFreeMemory(free);
load_builder.setMaxMemory(max);
try {
loadingNode.setContent(load_builder.build().toString());
logger.info(String.format("Reporting loading: free memory: %d, total memory: %d max memory: %d", free, total, max));
}
catch(CODIException e) {
logger.error("Cannot report loading", e);
}
}
Thread.sleep(5000);
}
}
public static void main(String argv[]) throws Exception {
String mqHome = System.getProperty("com.trendmicro.tme.broker.mqhome", "/opt/trend/tme");
String mqVar = System.getProperty("com.trendmicro.tme.broker.mqvar", "/var/lib/tme/broker");
String mqLib = System.getProperty("com.trendmicro.tme.broker.mqlib", "/opt/trend/tme/lib");
String configPath = System.getProperty("com.trendmicro.tme.broker.config", "/opt/trend/tme/conf/broker/config.properties");
Properties config = new Properties();
FileInputStream fis = new FileInputStream(configPath);
config.load(fis);
IOUtils.closeQuietly(fis);
ZKSessionManager.initialize(config.getProperty("com.trendmicro.tme.broker.zookeeper.quorum"), Integer.valueOf(config.getProperty("com.trendmicro.tme.broker.zookeeper.timeout")));
final EmbeddedOpenMQ mq = new EmbeddedOpenMQ(mqHome, mqVar, mqLib, config);
mq.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
mq.stop();
try {
mq.unregisterOnZk();
}
catch(CODIException e) {
logger.error("Unable to unregister broker", e);
}
}
});
mq.registerOnZk();
mq.reportLoadingForever();
}
@Override
public synchronized void onDataChanged(String arg0, Map<String, byte[]> changeMap) {
if(changeMap.get("") == null) // broker node is removed
return;
statusChanged = true;
logger.info("broker status changed");
}
}