Java tutorial
// ======================================================================== // 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.diameter; import java.io.File; import java.io.FileInputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; 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.callflow.JmxMessageLog; import org.cipango.callflow.diameter.DiameterMessageFormator.Output; import org.cipango.diameter.Dictionary; import org.cipango.diameter.base.Accounting; import org.cipango.diameter.base.Common; import org.cipango.diameter.ims.Cx; import org.cipango.diameter.ims.IMS; import org.cipango.diameter.ims.Sh; import org.cipango.diameter.io.Codecs; import org.cipango.diameter.log.DiameterMessageListener; import org.cipango.diameter.node.DiameterConnection; import org.cipango.diameter.node.DiameterMessage; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class JmxMessageLogger extends AbstractLifeCycle implements DiameterMessageListener { private static final Logger LOG = Log.getLogger(JmxMessageLog.class); private static final int DEFAULT_MAX_MESSAGES = 50; public enum Direction { IN, OUT }; private MessageInfo[] _messages; private int _maxMessages = DEFAULT_MAX_MESSAGES; private int _cursor; 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 messageReceived(DiameterMessage message, DiameterConnection connection) { doLog(message, Direction.IN, connection); } public void messageSent(DiameterMessage message, DiameterConnection connection) { doLog(message, Direction.OUT, connection); } public void doLog(DiameterMessage message, Direction direction, DiameterConnection connection) { if (_messages != null) { synchronized (this) { _messages[_cursor] = new MessageInfo(message, direction, connection); _cursor = getNextCursor(); } } } 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()][5]; for (int i = 0; i < tab.length; i++) { MessageInfo info = (MessageInfo) messages.get(i); tab[i][0] = generateInfoLine(info); Output output = DiameterMessageFormator.getPretty().newOutput(); output.add(info.getMessage()); tab[i][1] = output.toString(); tab[i][2] = info.getRemote(); } return tab; } public String generateInfoLine(MessageInfo messageInfo) { StringBuilder sb = new StringBuilder(); sb.append(messageInfo.getFormatedDate()); if (messageInfo.getDirection() == Direction.IN) sb.append(" IN "); else sb.append(" OUT "); sb.append(messageInfo.getConnection().getLocalAddr()); sb.append(':'); sb.append(messageInfo.getConnection().getLocalPort()); sb.append((messageInfo.getDirection() == Direction.IN ? " < " : " > ")); sb.append(messageInfo.getConnection().getRemoteAddr()); sb.append(':'); sb.append(messageInfo.getConnection().getRemotePort()); sb.append('\n'); return sb.toString(); } @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 static void main(String[] args) throws Exception { Dictionary.getInstance().load(Common.class); Dictionary.getInstance().load(Accounting.class); Dictionary.getInstance().load(IMS.class); Dictionary.getInstance().load(Cx.class); Dictionary.getInstance().load(Sh.class); JmxMessageLogger logger = new JmxMessageLogger(); logger.start(); DiameterMessage message = Codecs.__message.decode(load("mar.dat")); message.getAVPs().add(Cx.CX_APPLICATION_ID.getAVP()); //((DiameterAnswer) message).setResultCode(Common.DIAMETER_SUCCESS); Output output = DiameterMessageFormator.getPretty().newOutput(); output.add(message); System.out.println(output.toString()); } protected static Buffer load(String name) throws Exception { URL url = JmxMessageLogger.class.getClassLoader().getResource(name); File file = new File(url.toURI()); FileInputStream fin = new FileInputStream(file); byte[] b = new byte[(int) file.length()]; fin.read(b); return new ByteArrayBuffer(b); } 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"); } } }