<commit_before>package com.plpatterns.status;

import static org.apache.commons.lang.StringUtils.isBlank;

import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.Callable;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Message;

/**
 * @author <NAME>
 */
public class XmppAppender extends AppenderSkeleton {

  private static final Log LOG = LogFactory.getLog(XmppAppender.class);
  
  private static final int MAX_CONVERSATIONS = 50;

  // TODO: {change this to a timestamp, and retry after a while. Ideally,
  // exponential back-off.}
  private boolean _notificationFailedRecently;

  private String _serverHostname;
  private int _serverPort;
  private String _serviceName;
  private ConnectionConfiguration _conConfig;
  private String _fromAddress;
  private String _password;
  private String _resource;
  private XMPPConnection _con;
  private LinkedList<Conversation> _conversations = new LinkedList<Conversation>();
  
  public XmppAppender() {
    // Default constructor for log4j.
    LOG.warn("XMPP Appender created");
  }
  
  public XmppAppender(String serverHostname, int serverPort,
          String serviceName, String fromAddress, String password,
          String resource) {
    this(new ConnectionConfiguration(serverHostname, serverPort, serviceName),
            fromAddress, password, resource);
  }
  
  public XmppAppender(ConnectionConfiguration conConfig, String fromAddress,
          String password, String resource) {
    _conConfig = conConfig;
    _fromAddress = fromAddress;
    _password = password;
    _resource = resource;
    LOG.warn("XMPP Appender created with good constructor!");
  }

  public void connect() {
    LOG.warn("Trying to connect...");

    // Must synchronize since this gets called from multiple threads.
    synchronized (this) {
      XMPPConnection con = getConnection();
      if (con == null) {
        LOG.debug("created new XMPPConnection object");
        con = new XMPPConnection(getConnectionConfiguration());
        setConnection(con);
      }

      LOG.warn("Authenticated? " + con.isAuthenticated() + " con: " + _con);
      if (con.isAuthenticated()) return;

      try {
        // Connect to server.
        LOG.warn("Connecting...");
        con.connect();

        // Add listener to capture IMs sent to us.
        LOG.warn("Adding chat listener...");
        con.getChatManager().addChatListener(new XmppChatManagerListener());

        // Login.
        LOG.warn("Logging in...");
        SASLAuthentication.supportSASLMechanism("PLAIN", 0);
        con.login(getFromAddress(), getPassword(), getResource());

        LOG.warn("Logged in!  Authenticated? " + con.isAuthenticated());
      }
      catch (Throwable t) {
        LOG.warn("connecting and logging in to XMPP server failed using connection configuration host: " + _conConfig.getHost()
                + " port: " + _conConfig.getPort() + " serviceName: " + _conConfig.getServiceName(),
                t);
        setNotificationFailedRecently(true);
      }
    }
  }

  @Override
  public void close() {
    if (getConnection() != null) {
      LOG.debug("Disconnecting...");
      getConnection().disconnect();
    }
  }

  @Override
  protected void append(LoggingEvent event) {
    if (!shouldNotify()) return;

    String msg = getMessage(event);
    if (isBlank(msg)) return;

    // Return if we have not connected or logged in yet.
    if (getConnection() == null || !getConnection().isAuthenticated()) {
      if (LOG.isDebugEnabled()) {
        LOG.debug(String.format(
                "Dropping notification since we are not authenticated.  Did you remember to call connect()?  If you did call connect(), this could be a connection problem with the server.  msg: %s, conConfig",
                msg,
                _conConfig));
      }
      return;
    }

    // TODO: Should the failure of one message prevent the others from being attempted?
    try {
      // Send notification to all conversations.
      for (Conversation conversation : getConversations()) {
        if (!conversation.shouldNotify(event)) continue;
        
        // Send the IM.
        conversation.sendIm(msg, true);
      }
    }
    catch (Throwable t) {
      // We had an error in sending the message.  Don't try to send again.
      setNotificationFailedRecently(true);
    }
  }


  /**
   * Gets the message of an event. If the message is a {@link Callable}, calls
   * it and returns {@code toString()} of the result. Otherwise simply calls
   * {@code toString()} of the message.
   */
  private static String getMessage(LoggingEvent event) {
    Object msg = event.getMessage();
    
    if (msg instanceof Callable) {
      try {
        return ObjectUtils.toString(((Callable<?>)msg).call());
      }
      catch (Throwable t) {
        return "";
      }
    }
    
    return ObjectUtils.toString(msg);
  }


  /**
   * As far as I understand, there is no way using log4j to know when all the<commit_msg>Added type parameter.<commit_after>package com.plpatterns.status;

import static org.apache.commons.lang.StringUtils.isBlank;

import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.Callable;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Message;

/**
 * @author <NAME>
 */
public class XmppAppender extends AppenderSkeleton {

  private static final Log LOG = LogFactory.getLog(XmppAppender.class);
  
  private static final int MAX_CONVERSATIONS = 50;

  // TODO: {change this to a timestamp, and retry after a while. Ideally,
  // exponential back-off.}
  private boolean _notificationFailedRecently;

  private String _serverHostname;
  private int _serverPort;
  private String _serviceName;
  private ConnectionConfiguration _conConfig;
  private String _fromAddress;
  private String _password;
  private String _resource;
  private XMPPConnection _con;
  private LinkedList<Conversation> _conversations = new LinkedList<Conversation>();
  
  public XmppAppender() {
    // Default constructor for log4j.
    LOG.warn("XMPP Appender created");
  }
  
  public XmppAppender(String serverHostname, int serverPort,
          String serviceName, String fromAddress, String password,
          String resource) {
    this(new ConnectionConfiguration(serverHostname, serverPort, serviceName),
            fromAddress, password, resource);
  }
  
  public XmppAppender(ConnectionConfiguration conConfig, String fromAddress,
          String password, String resource) {
    _conConfig = conConfig;
    _fromAddress = fromAddress;
    _password = password;
    _resource = resource;
    LOG.warn("XMPP Appender created with good constructor!");
  }

  public void connect() {
    LOG.warn("Trying to connect...");

    // Must synchronize since this gets called from multiple threads.
    synchronized (this) {
      XMPPConnection con = getConnection();
      if (con == null) {
        LOG.debug("created new XMPPConnection object");
        con = new XMPPConnection(getConnectionConfiguration());
        setConnection(con);
      }

      LOG.warn("Authenticated? " + con.isAuthenticated() + " con: " + _con);
      if (con.isAuthenticated()) return;

      try {
        // Connect to server.
        LOG.warn("Connecting...");
        con.connect();

        // Add listener to capture IMs sent to us.
        LOG.warn("Adding chat listener...");
        con.getChatManager().addChatListener(new XmppChatManagerListener());

        // Login.
        LOG.warn("Logging in...");
        SASLAuthentication.supportSASLMechanism("PLAIN", 0);
        con.login(getFromAddress(), getPassword(), getResource());

        LOG.warn("Logged in!  Authenticated? " + con.isAuthenticated());
      }
      catch (Throwable t) {
        LOG.warn("connecting and logging in to XMPP server failed using connection configuration host: " + _conConfig.getHost()
                + " port: " + _conConfig.getPort() + " serviceName: " + _conConfig.getServiceName(),
                t);
        setNotificationFailedRecently(true);
      }
    }
  }

  @Override
  public void close() {
    if (getConnection() != null) {
      LOG.debug("Disconnecting...");
      getConnection().disconnect();
    }
  }

  @Override
  protected void append(LoggingEvent event) {
    if (!shouldNotify()) return;

    String msg = getMessage(event);
    if (isBlank(msg)) return;

    // Return if we have not connected or logged in yet.
    if (getConnection() == null || !getConnection().isAuthenticated()) {
      if (LOG.isDebugEnabled()) {
        LOG.debug(String.format(
                "Dropping notification since we are not authenticated.  Did you remember to call connect()?  If you did call connect(), this could be a connection problem with the server.  msg: %s, conConfig",
                msg,
                _conConfig));
      }
      return;
    }

    // TODO: Should the failure of one message prevent the others from being attempted?
    try {
      // Send notification to all conversations.
      for (Conversation conversation : getConversations()) {
        if (!conversation.shouldNotify(event)) continue;
        
        // Send the IM.
        conversation.sendIm(msg, true);
      }
    }
    catch (Throwable t) {
      // We had an error in sending the message.  Don't try to send again.
      setNotificationFailedRecently(true);
    }
  }


  /**
   * Gets the message of an event. If the message is a {@link Callable}, calls
   * it and returns {@code toString()} of the result. Otherwise simply calls
   * {@code toString()} of the message.
   */
  private static String getMessage(LoggingEvent event) {
    Object msg = event.getMessage();
    
    if (msg instanceof Callable<?>) {
      try {
        return ObjectUtils.toString(((Callable<?>)msg).call());
      }
      catch (Throwable t) {
        return "";
      }
    }
    
    return ObjectUtils.toString(msg);
  }


  /**
   * As far as I understand, there is no way using log4j to know when all the
{'fraction_non_alphanumeric': 0.10457870495800596, 'fraction_numerical': 0.0014449561997651947, 'mean_word_length': 3.0036153289949383, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6030923', 'n_tokens_mistral': 3162, 'n_tokens_neox': 3103, 'n_words': 871}