package org.act.tstream.daemon.supervisor; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.RandomAccessFile; import java.net.BindException; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.URI; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import backtype.storm.daemon.Shutdownable; import org.act.tstream.client.ConfigExtension; import org.act.tstream.daemon.worker.Worker; import org.act.tstream.utils.FileAttribute; import org.act.tstream.utils.HttpserverUtils; import org.act.tstream.utils.JStormUtils; import org.act.tstream.utils.Pair; import org.act.tstream.utils.TimeFormat; import com.google.common.base.Joiner; import com.google.common.collect.Maps; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; public class Httpserver implements Shutdownable { private static Logger LOG = Logger.getLogger(Httpserver.class); private HttpServer hs; private int port; private Map conf; public Httpserver(int port, Map conf) { this.port = port; this.conf = conf; } static class LogHandler implements HttpHandler { private String logDir; Map conf; public LogHandler(Map conf) { logDir = JStormUtils.getLogDir(); this.conf = conf; LOG.info("logview logDir=" + logDir); // +++ } public void handlFailure(HttpExchange t, String errorMsg) throws IOException { LOG.error(errorMsg); byte[] data = errorMsg.getBytes(); t.sendResponseHeaders(HttpURLConnection.HTTP_BAD_REQUEST, data.length); OutputStream os = t.getResponseBody(); os.write(data); os.close(); } public void handle(HttpExchange t) throws IOException { URI uri = t.getRequestURI(); Map<String, String> paramMap = parseRawQuery(uri.getRawQuery()); String cmd = paramMap .get(HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_CMD); if (StringUtils.isBlank(cmd) == true) { handlFailure(t, "Bad Request, Not set command type"); return; } if (HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_CMD_SHOW.equals(cmd)) { handleShowLog(t, paramMap); return; } else if (HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_CMD_LIST .equals(cmd)) { handleListDir(t, paramMap); return; }else if (HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_CMD_JSTACK.equals(cmd)) { handleJstack(t, paramMap); return ; } handlFailure(t, "Bad Request, Not support command type " + cmd); return; } private Map<String, String> parseRawQuery(String uriRawQuery) { Map<String, String> paramMap = Maps.newHashMap(); for (String param : StringUtils.split(uriRawQuery, "&")) { String[] pair = StringUtils.split(param, "="); if (pair.length == 2) { paramMap.put(pair[0], pair[1]); } } return paramMap; } private void handleShowLog(HttpExchange t, Map<String, String> paramMap) throws IOException { Pair<Long, byte[]> logPair = queryLog(t, paramMap); if (logPair == null) { return; } String size = String.format( HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_SIZE_FORMAT, logPair.getFirst()); byte[] sizeByts = size.getBytes(); byte[] logData = logPair.getSecond(); t.sendResponseHeaders(HttpURLConnection.HTTP_OK, sizeByts.length + logData.length); OutputStream os = t.getResponseBody(); os.write(sizeByts); os.write(logData); os.close(); } private Pair<Long, byte[]> queryLog(HttpExchange t, Map<String, String> paramMap) throws IOException { String fileParam = paramMap .get(HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_LOGFILE); if (StringUtils.isBlank(fileParam)) { handlFailure(t, "Bad Request, Params Error, no log file name."); return null; } String logFile = Joiner.on(File.separator).join(logDir, fileParam); FileChannel fc = null; MappedByteBuffer fout = null; long fileSize = 0; byte[] ret = "Failed to get data".getBytes(); try { fc = new RandomAccessFile(logFile, "r").getChannel(); fileSize = fc.size(); long position = fileSize - HttpserverUtils.HTTPSERVER_LOGVIEW_PAGESIZE; try { String posStr = paramMap .get(HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_POS); if (StringUtils.isBlank(posStr) == false) { long pos = Long.valueOf(posStr); position = pos; } } catch (Exception e) { LOG.warn("Invalide position "); } if (position < 0) { position = 0L; } long size = Math.min(fileSize - position, HttpserverUtils.HTTPSERVER_LOGVIEW_PAGESIZE); LOG.info("logview " + logFile + ", position=" + position + ", size=" + size); fout = fc.map(FileChannel.MapMode.READ_ONLY, position, size); ret = new byte[(int) size]; fout.get(ret); String str = new String(ret, ConfigExtension.getLogViewEncoding(conf)); return new Pair<Long, byte[]>(fileSize, str.getBytes()); } catch (FileNotFoundException e) { LOG.warn(e); handlFailure(t, "Bad Request, Failed to find " + fileParam); return null; } catch (IOException e) { LOG.warn(e); handlFailure(t, "Bad Request, Failed to open " + fileParam); return null; } finally { fout = null; if (fc != null) { IOUtils.closeQuietly(fc); } } } byte[] getJSonFiles(String dir) throws Exception { Map<String, FileAttribute> fileMap = new HashMap<String, FileAttribute>(); String path = logDir; if (dir != null) { path = path + File.separator + dir; } LOG.info("List dir " + path); File file = new File(path); String[] files = file.list(); for (String fileName : files) { String logFile = Joiner.on(File.separator).join(path, fileName); FileAttribute fileAttribute = new FileAttribute(); fileAttribute.setFileName(fileName); File subFile = new File(logFile); Date modify = new Date(subFile.lastModified()); fileAttribute.setModifyTime(TimeFormat.getSecond(modify)); if (subFile.isFile()) { fileAttribute.setIsDir(String.valueOf(false)); fileAttribute.setSize(String.valueOf(subFile.length())); fileMap.put(logFile, fileAttribute); } else if (subFile.isDirectory()) { fileAttribute.setIsDir(String.valueOf(true)); fileAttribute.setSize(String.valueOf(4096)); fileMap.put(logFile, fileAttribute); } } String fileJsonStr = JStormUtils.to_json(fileMap); return fileJsonStr.getBytes(); } void handleListDir(HttpExchange t, Map<String, String> paramMap) throws IOException { byte[] filesJson = "Failed to get file list".getBytes(); try { String dir = paramMap.get(HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_DIR); filesJson = getJSonFiles(dir); } catch (Exception e) { handlFailure(t, "Failed to get file list"); return; } t.sendResponseHeaders(HttpURLConnection.HTTP_OK, filesJson.length); OutputStream os = t.getResponseBody(); os.write(filesJson); os.close(); } void handleJstack(StringBuffer sb, Integer pid) { String cmd = "jstack " + pid; try { LOG.info("Begin to execute " + cmd); Process process = JStormUtils.launch_process(cmd, new HashMap<String, String>(), false); // Process process = Runtime.getRuntime().exec(sb.toString()); InputStream stdin = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader( stdin)); JStormUtils.sleepMs(1000); // if (process.exitValue() != 0) { // LOG.info("Failed to execute " + sb.toString()); // return null; // } String str; while ((str = reader.readLine()) != null) { if (StringUtils.isBlank(str)) { // LOG.info(str + " is Blank"); continue; } sb.append(str).append("\r\n"); } LOG.info("Successfully get output of " + cmd); return ; } catch (IOException e) { LOG.info("Failed to execute " + cmd, e); sb.append("Failed to execute " + cmd); return ; } catch (Exception e) { LOG.info(e.getCause(), e); sb.append("Failed to execute " + cmd + ", " + e.getCause()); return ; } } void handleJstack(HttpExchange t, Map<String, String> paramMap) throws IOException { String workerPort = paramMap.get( HttpserverUtils.HTTPSERVER_LOGVIEW_PARAM_WORKER_PORT); if (workerPort == null) { handlFailure(t, "Not set worker's port"); return ; } LOG.info("Begin to get jstack of " + workerPort); StringBuffer sb = new StringBuffer(); List<Integer> pids = Worker.getOldPortPids(workerPort); for (Integer pid : pids) { sb.append("!!!!!!!!!!!!!!!!!!\r\n"); sb.append("WorkerPort:" + workerPort + ", pid:" + pid); sb.append("\r\n!!!!!!!!!!!!!!!!!!\r\n"); handleJstack(sb, pid); } byte[] data = sb.toString().getBytes(); t.sendResponseHeaders(HttpURLConnection.HTTP_OK, data.length); OutputStream os = t.getResponseBody(); os.write(data); os.close(); } }// LogHandler public void start() { int numHandler = 3; InetSocketAddress socketAddr = new InetSocketAddress(port); Executor executor = Executors.newFixedThreadPool(numHandler); try { hs = HttpServer.create(socketAddr, 0); hs.createContext(HttpserverUtils.HTTPSERVER_CONTEXT_PATH_LOGVIEW, new LogHandler(conf)); hs.setExecutor(executor); hs.start(); } catch (BindException e) { LOG.info("Httpserver Already start!"); hs = null; return; } catch (IOException e) { LOG.error("Httpserver Start Failed", e); hs = null; return; } LOG.info("Success start httpserver at port:" + port); } @Override public void shutdown() { if (hs != null) { hs.stop(0); LOG.info("Successfully stop http server"); } } }