content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.hadoop.chukwa.datacollection.collector.servlet;
  
  import javax.servlet.ServletConfig;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  import org.apache.log4j.Logger;
  
  import java.io.*;
  import java.nio.charset.Charset;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.util.*;
  
  import org.apache.hadoop.chukwa.*;
  import org.apache.hadoop.chukwa.datacollection.writer.ExtractorWriter;
  import org.apache.hadoop.conf.Configuration;
  
  @Deprecated
  public class LogDisplayServlet extends HttpServlet {
    
    /*
    static class StreamName {
      byte[] md5;
      public StreamName(Chunk c) {
    
      }
      @Override
      public int hashCode() {
        int x=0;
        for(int i=0; i< md5.length; ++i) {
          x ^= (md5[i] << 4 * i);
        }
        return x;
      }
      
      public boolean equals(Object x) {
        if(x instanceof StreamName)
          return Arrays.equals(md5, ((StreamName)x).md5);
        else return false;
      }
    }*/
    
    public static final String DEFAULT_PATH = "logs";
    public static final String ENABLED_OPT = "chukwaCollector.showLogs.enabled";
    public static final String BUF_SIZE_OPT = "chukwaCollector.showLogs.buffer";
    long BUF_SIZE = 1024* 1024;
    
    transient Configuration conf;
    transient Map<String, Deque<Chunk>> chunksBySID;
    Queue<String> receivedSIDs = new LinkedList<String>();
    long totalStoredSize = 0;
  
    private static final long serialVersionUID = -4602082382919009285L;
    protected final static Logger log = Logger.getLogger(LogDisplayServlet.class);
    
    public LogDisplayServlet() {
      conf = new Configuration();
      chunksBySID = new HashMap<String, Deque<Chunk>>();
      ExtractorWriter.setRecipient(this);
    }
    
    public LogDisplayServlet(Configuration c) {
      conf = c;
      chunksBySID = new HashMap<String, Deque<Chunk>>();
      ExtractorWriter.setRecipient(this);
    }
  
    public LogDisplayServlet(Configuration c, Map<String, Deque<Chunk>> chunksBySID) {
      conf = c;
      this.chunksBySID = chunksBySID;
      ExtractorWriter.setRecipient(this);
    }
  
    public void init(ServletConfig servletConf) throws ServletException {
      BUF_SIZE = conf.getLong(BUF_SIZE_OPT, BUF_SIZE);
    }
  
    @Override
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
      resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); 
    }
    
    private String getSID(Chunk c) {
      try { 
        MessageDigest md;
        md = MessageDigest.getInstance("MD5");
    
        md.update(c.getSource().getBytes(Charset.forName("UTF-8")));
        md.update(c.getStreamName().getBytes(Charset.forName("UTF-8")));
        md.update(c.getTags().getBytes(Charset.forName("UTF-8")));
        StringBuilder sb = new StringBuilder();
        byte[] bytes = md.digest();
        for(int i=0; i < bytes.length; ++i) {
          if( (bytes[i] & 0xF0) == 0)
            sb.append('0');
          sb.append( Integer.toHexString(0xFF & bytes[i]) );
        }
        return sb.toString();
      } catch(NoSuchAlgorithmException n) {
        log.fatal(n);
        return null;
      }
    }
    
    
    private void pruneOldEntries() {
      while(totalStoredSize > BUF_SIZE) {
        String queueToPrune = receivedSIDs.remove();
        Deque<Chunk> stream = chunksBySID.get(queueToPrune);
        assert !stream.isEmpty() : " expected a chunk in stream with ID " + queueToPrune;
        Chunk c = stream.poll();
        if(c != null)
          totalStoredSize -= c.getData().length;
        if(stream.isEmpty()) {  //remove empty deques and their names.
          chunksBySID.remove(queueToPrune);
        }
      }
    }
    
    public synchronized void add(List<Chunk> chunks) {
      for(Chunk c : chunks) {
        String sid = getSID(c);
        Deque<Chunk> stream = chunksBySID.get(sid);
        if(stream == null) {
          stream = new LinkedList<Chunk>();
          chunksBySID.put(sid, stream);
        }
        stream.add(c);
        receivedSIDs.add(sid);
        totalStoredSize += c.getData().length;
      }
      pruneOldEntries();
    }
    
  
    @Override
    protected synchronized void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException  {
    
      PrintStream out = new PrintStream(new BufferedOutputStream(resp.getOutputStream()), true, "UTF-8");
      resp.setStatus(200);
      String path = req.getServletPath();
      String streamID = req.getParameter("sid");
      if (streamID != null) {
        try {
          Deque<Chunk> chunks = chunksBySID.get(streamID);
          if(chunks != null) {
            String streamName = getFriendlyName(chunks.peek());
            out.println("<html><title>Chukwa:Received Data</title><body><h2>Data from "+ streamName + "</h2>");
            out.println("<pre>");
            for(Chunk c: chunks) {
              out.write(c.getData());
            }
            out.println("</pre><hr><a href=\""+path+"\">Back to list of streams</a>");
          } else
            out.println("No data");
        } catch(Exception e) {
          out.println("<html><body>No data</body></html>");
        }
        out.println("</body></html>");
      } else {
        out.println("<html><title>Chukwa:Received Data</title><body><h2>Recently-seen streams</h2><ul>");
        for(Map.Entry<String, Deque<Chunk>> sid: chunksBySID.entrySet()) 
          out.println("<li> <a href=\"" + path + "?sid="+sid.getKey() + "\">"+ getFriendlyName(sid.getValue().peek()) + "</a></li>");
        out.println("</ul></body></html>");
      }
      out.flush();
    }
  
    private String getFriendlyName(Chunk chunk) {
      if(chunk != null)
        return chunk.getTags() + "/" + chunk.getSource() + "/" + chunk.getStreamName();
      else return "null";
    }
  
  
  }
  