org.cipango.callflow.JmxMessageLog.java Source code

Java tutorial

Introduction

Here is the source code for org.cipango.callflow.JmxMessageLog.java

Source

// ========================================================================
// Copyright 2010 NEXCOM Systems
// ------------------------------------------------------------------------
// Licensed 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.cipango.callflow;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;

import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;

import org.apache.commons.jexl.Expression;
import org.apache.commons.jexl.ExpressionFactory;
import org.apache.commons.jexl.JexlContext;
import org.apache.commons.jexl.JexlHelper;
import org.cipango.server.SipConnection;
import org.cipango.server.SipMessage;
import org.cipango.server.log.AbstractMessageLog;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;

public class JmxMessageLog extends AbstractMessageLog implements NotificationEmitter {
    private static final Logger LOG = Log.getLogger(JmxMessageLog.class);

    private static final int DEFAULT_MAX_MESSAGES = 100;

    private MessageInfo[] _messages;
    private int _maxMessages = DEFAULT_MAX_MESSAGES;
    private int _cursor;
    private long _messageId = 0;

    private Map<String, String> _alias = new HashMap<String, String>();

    private List<ListenerInfo> _listeners = new ArrayList<JmxMessageLog.ListenerInfo>();

    public int getMaxMessages() {
        return _maxMessages;
    }

    public void setMaxMessages(int maxMessages) {
        if (maxMessages <= 0)
            throw new IllegalArgumentException("Max message must be greater than 0");
        synchronized (this) {
            if (isRunning() && maxMessages != _maxMessages) {
                MessageInfo[] messages = new MessageInfo[maxMessages];
                ListIterator<MessageInfo> it = iterate(false);
                int index = maxMessages;
                while (it.hasPrevious()) {
                    messages[--index] = it.previous();
                    if (index == 0)
                        break;
                }
                _cursor = 0;
                _messages = messages;
            }
            _maxMessages = maxMessages;
        }
    }

    protected void doStart() throws Exception {
        _messages = new MessageInfo[_maxMessages];
        _cursor = 0;
        super.doStart();
    }

    protected void doStop() throws Exception {
        _messages = null;
        super.doStop();
    }

    public void doLog(SipMessage message, int direction, SipConnection connection) {
        if (_messages != null) {
            // Log only once message to loopback
            if (direction == OUT && connection.getLocalAddress().equals(connection.getRemoteAddress())
                    && connection.getLocalPort() == connection.getRemotePort())
                return;

            MessageInfo messageInfo = new MessageInfo(message, direction, connection);
            synchronized (this) {
                _messages[_cursor] = messageInfo;
                _cursor = getNextCursor();
                _messageId++;
            }
            if (!_listeners.isEmpty()) {
                String infoLine;
                synchronized (this) {
                    infoLine = generateInfoLine(direction, connection, System.currentTimeMillis());
                }
                CallflowNotification notification = new CallflowNotification(messageInfo, _messageId, infoLine);
                sendNotification(notification);
            }
        }
    }

    public Object[][] getMessages(Integer maxMessages) throws Exception {
        return getMessages(maxMessages, null);
    }

    private ListIterator<MessageInfo> iterate(boolean start) {
        return new LogIterator(start);
    }

    private int getNextCursor() {
        return _cursor + 1 == _maxMessages ? 0 : _cursor + 1;
    }

    public void clear() {
        if (_messages == null)
            return;

        synchronized (this) {
            for (int i = 0; i < _messages.length; i++)
                _messages[i] = null;
            _cursor = 0;
        }
    }

    public Object[][] getMessages(Integer maxMessages, String msgFilter) throws Exception {
        List<MessageInfo> messages = getMessageList(maxMessages, msgFilter);
        Object[][] tab = new Object[messages.size()][3];
        for (int i = 0; i < tab.length; i++) {
            MessageInfo info = (MessageInfo) messages.get(i);
            tab[i][0] = generateInfoLine(info.getDirection(), info.getConnection(), info.getDate());
            tab[i][1] = info.getMessage();
            tab[i][2] = info.getRemote();
        }

        return tab;
    }

    @SuppressWarnings("unchecked")
    private List<MessageInfo> getMessageList(Integer maxMessages, String msgFilter) throws Exception {
        if (_messages == null)
            return null;

        synchronized (this) {
            JexlContext jc = JexlHelper.createContext();
            Expression msgExpression = null;
            if (msgFilter != null && !msgFilter.trim().equals("")) {
                LOG.debug("Get messages with filter: " + msgFilter);
                msgExpression = ExpressionFactory.createExpression("log." + msgFilter);
            }

            List<MessageInfo> result = new ArrayList<MessageInfo>();
            ListIterator<MessageInfo> it = iterate(false);

            int i = 0;
            while (it.hasPrevious() && i < maxMessages) {
                MessageInfo info = it.previous();
                jc.getVars().put("log", info);
                jc.getVars().put("message", info.getMessage());

                if (msgExpression == null || ((Boolean) msgExpression.evaluate(jc)).booleanValue()) {
                    result.add(0, info);
                    i++;
                }
            }
            return result;
        }
    }

    public byte[] generateGraph(Integer maxMessages, String msgFilter, String xslUri) throws Exception {
        return generateGraph(getMessageList(maxMessages, msgFilter), xslUri, false);
    }

    public byte[] generateGraph(Integer maxMessages, String msgFilter, String xslUri, Boolean includeMsg)
            throws Exception {
        return generateGraph(getMessageList(maxMessages, msgFilter), xslUri, includeMsg);
    }

    protected byte[] generateGraph(List<MessageInfo> messages, String xslUri, boolean includeMsg)
            throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        OutputStreamWriter out = new OutputStreamWriter(os);
        out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
        if (xslUri != null)
            out.write("<?xml-stylesheet href=\"" + xslUri + "\" type=\"text/xsl\"?>\n");
        out.write("<data>\n");
        out.write("\t<hosts>\n");
        Map<String, Integer> hostsMap = new HashMap<String, Integer>();
        Iterator<MessageInfo> it = messages.iterator();
        int indexLocal = -1;
        int index = 1;
        while (it.hasNext()) {
            MessageInfo info = it.next();
            if (!hostsMap.containsKey(info.getLocalKey())) {
                if (indexLocal == -1) {
                    out.write("\t\t<host>");
                    String alias = _alias.get(info.getLocalKey());
                    out.write(alias == null ? "Cipango" : alias);
                    out.write("</host>\n");
                    indexLocal = index++;
                }
                hostsMap.put(info.getLocalKey(), indexLocal);
            }

            if (!hostsMap.containsKey(info.getRemoteKey())) {
                out.write("\t\t<host>");
                String alias = _alias.get(info.getRemoteKey());
                out.write(alias == null ? info.getRemote() : alias);
                out.write("</host>\n");
                hostsMap.put(info.getRemoteKey(), index++);
            }
        }
        out.write("\t</hosts>\n");

        it = messages.iterator();
        out.write("\t<messages>\n");
        while (it.hasNext()) {
            MessageInfo info = it.next();
            out.write("\t\t<message from=\"");
            if (info.getDirection() == IN) {
                out.write(String.valueOf(hostsMap.get(info.getRemoteKey())));
                out.write("\" to=\"");
                out.write(String.valueOf(hostsMap.get(info.getLocalKey())));
                out.write("\">\n");
            } else {
                out.write(String.valueOf(hostsMap.get(info.getLocalKey())));
                out.write("\" to=\"");
                out.write(String.valueOf(hostsMap.get(info.getRemoteKey())));
                out.write("\">\n");
            }
            out.write("\t\t\t<name>");
            out.write(info.getShortName());
            out.write("</name>\n");
            out.write("\t\t\t<date>");
            out.write(info.getFormatedDate());
            out.write("</date>\n");
            if (includeMsg) {
                StringBuilder sb = new StringBuilder(info.getMessage().toString());
                replaceAll(sb, "<", "&lt;");
                replaceAll(sb, ">", "&gt;");
                String msg = sb.toString();
                int nbLines = 1;
                for (int i = 0; i < msg.length(); i++)
                    if (msg.charAt(i) == '\n')
                        nbLines++;
                out.write("\t\t\t<content nbLines=\"" + nbLines + "\">");
                out.write(msg);
                out.write("</content>\n");
            }
            out.write("\t\t</message>\n");
        }
        out.write("\t</messages>\n");
        out.write("</data>\n");
        out.flush();
        return os.toByteArray();
    }

    protected void replaceAll(StringBuilder sb, String toFind, Object toSet) {
        int index = 0;
        while ((index = sb.indexOf(toFind)) != -1)
            sb.replace(index, index + toFind.length(), toSet.toString());
    }

    public void addAlias(String host, int port, String name) throws UnknownHostException {
        InetAddress addr = InetAddress.getByName(host);
        _alias.put(addr.getHostAddress() + ":" + port, name);
    }

    private class LogIterator implements ListIterator<MessageInfo> {
        private int _itCursor;
        private boolean _start = true;

        public LogIterator(boolean start) {
            if (start)
                _itCursor = _messages[getNextCursor()] == null ? 0 : getNextCursor();
            else
                _itCursor = _cursor;
        }

        private int getNextItCursor() {
            return _itCursor + 1 == _maxMessages ? 0 : _itCursor + 1;
        }

        private int getPreviousItCursor() {
            return _itCursor == 0 ? _maxMessages - 1 : _itCursor - 1;
        }

        public boolean hasNext() {
            return _itCursor != _cursor && _messages[getNextItCursor()] != null;
        }

        public MessageInfo next() {
            if (!hasNext())
                throw new NoSuchElementException("No next");
            _itCursor = getNextItCursor();
            return _messages[_itCursor];
        }

        public void remove() {
            throw new UnsupportedOperationException("Read-only");
        }

        public void add(MessageInfo arg0) {
            throw new UnsupportedOperationException("Read-only");
        }

        public boolean hasPrevious() {
            return (_start || _itCursor != _cursor) && _messages[getPreviousItCursor()] != null;
        }

        public int nextIndex() {
            return 0;
        }

        public MessageInfo previous() {
            if (!hasPrevious())
                throw new NoSuchElementException("No previous");
            _start = false;
            _itCursor = getPreviousItCursor();
            return _messages[_itCursor];
        }

        public int previousIndex() {
            return 0;
        }

        public void set(MessageInfo arg0) {
            throw new UnsupportedOperationException("Read-only");
        }
    }

    public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback)
            throws IllegalArgumentException {
        synchronized (_listeners) {
            _listeners.add(new ListenerInfo(listener, filter, handback));
        }
    }

    public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
        synchronized (_listeners) {
            Iterator<ListenerInfo> it = _listeners.iterator();
            while (it.hasNext()) {
                JmxMessageLog.ListenerInfo info = it.next();
                if (info.listener.equals(listener)) {
                    it.remove();
                    return;
                }
            }
        }
        throw new ListenerNotFoundException();
    }

    public void removeNotificationListener(NotificationListener listener, NotificationFilter filter,
            Object handback) throws ListenerNotFoundException {
        synchronized (_listeners) {
            Iterator<ListenerInfo> it = _listeners.iterator();
            while (it.hasNext()) {
                JmxMessageLog.ListenerInfo info = it.next();
                if (info.listener.equals(listener) && info.filter == filter && info.handback == handback) {
                    it.remove();
                    return;
                }
            }
        }
        throw new ListenerNotFoundException();
    }

    public MBeanNotificationInfo[] getNotificationInfo() {
        String[] types = new String[] { "SIP" };
        String name = MBeanNotificationInfo.class.getName();
        String description = "SIP message notification";
        MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description);

        return new MBeanNotificationInfo[] { info };
    }

    private void sendNotification(Notification notification) {
        if (notification == null || _listeners.isEmpty())
            return;

        synchronized (_listeners) {
            for (ListenerInfo info : _listeners) {
                if (info.filter == null || info.filter.isNotificationEnabled(notification)) {
                    try {
                        info.listener.handleNotification(notification, info.handback);
                    } catch (Exception e) {
                        LOG.warn(e);
                    }
                }
            }
        }
    }

    private class ListenerInfo {
        public NotificationListener listener;
        NotificationFilter filter;
        Object handback;

        public ListenerInfo(NotificationListener listener, NotificationFilter filter, Object handback) {
            this.listener = listener;
            this.filter = filter;
            this.handback = handback;
        }
    }

}