/*
* Copyright (C) 2005-2008 Jive Software. All rights reserved.
*
* 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.jivesoftware.openfire.plugin;
import java.io.File;
import java.util.Map;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.filterchain.IoFilter;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.container.PluginManagerListener;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.jivesoftware.openfire.spi.ConnectionManagerImpl.COMPRESSION_FILTER_NAME;
import static org.jivesoftware.openfire.spi.ConnectionManagerImpl.TLS_FILTER_NAME;
/**
* Debugger plugin that prints XML traffic to stdout. By default it will only print
* raw XML traffic (by using a MINA filter). To turn on printing of interpreted XML
* (i.e. parsed XML) just enable the system property <tt>plugin.debugger.interpretedAllowed</tt>.
* There is no need to restart the plugin or the server.
*
* @author Gaston Dombiak
*/
public class DebuggerPlugin implements Plugin, PropertyEventListener {
public static final Logger Log = LoggerFactory.getLogger( DebuggerPlugin.class );
private RawPrintFilter defaultPortFilter;
private RawPrintFilter oldPortFilter;
private RawPrintFilter componentPortFilter;
private RawPrintFilter multiplexerPortFilter;
private InterpretedXMLPrinter interpretedPrinter;
public void initializePlugin(final PluginManager pluginManager, final File pluginDirectory) {
if (pluginManager.isExecuted()) {
addInterceptors();
} else {
pluginManager.addPluginManagerListener(new PluginManagerListener() {
public void pluginsMonitored() {
// Stop listening for plugin events
pluginManager.removePluginManagerListener(this);
// Start listeners
addInterceptors();
}
});
}
}
protected void addFilterToChain( final SocketAcceptor acceptor, final String filterName, final IoFilter filter )
{
if ( acceptor == null )
{
Log.debug( "Not adding filter '{}' to acceptor that is null.", filterName );
return;
}
final DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
if ( chain.contains( COMPRESSION_FILTER_NAME ) )
{
Log.debug( "Adding filter '{}' as the first filter after the compression filter in acceptor {}", filterName, acceptor );
chain.addAfter( COMPRESSION_FILTER_NAME, filterName, filter );
}
else if ( chain.contains( TLS_FILTER_NAME ) )
{
Log.debug( "Adding filter '{}' as the first filter after the TLS filter in acceptor {}", filterName, acceptor );
chain.addAfter( TLS_FILTER_NAME, filterName, filter );
}
else
{
Log.debug( "Adding filter '{}' as the last filter in acceptor {}", filterName, acceptor );
chain.addLast( filterName, filter );
}
}
protected void removeFilterFromChain( final SocketAcceptor acceptor, final String filterName )
{
if ( acceptor == null )
{
Log.debug( "Not removing filter '{}' from acceptor that is null.", filterName );
return;
}
if ( acceptor.getFilterChain().contains( filterName ) )
{
Log.debug( "Removing filter '{}' from acceptor {}", filterName, acceptor );
acceptor.getFilterChain().remove( filterName );
}
else
{
Log.debug( "Unable to remove non-existing filter '{}' from acceptor {}", filterName, acceptor );
}
}
private void addInterceptors()
{
defaultPortFilter = new RawPrintFilter("C2S");
oldPortFilter = new RawPrintFilter("SSL");
componentPortFilter = new RawPrintFilter("ExComp");
multiplexerPortFilter = new RawPrintFilter("CM");
// Add filter to filter chain builder
final ConnectionManagerImpl connManager = (ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager();
addFilterToChain( connManager.getSocketAcceptor(), RawPrintFilter.FILTER_NAME, defaultPortFilter );
addFilterToChain( connManager.getSSLSocketAcceptor(), RawPrintFilter.FILTER_NAME, oldPortFilter );
addFilterToChain( connManager.getComponentAcceptor(), RawPrintFilter.FILTER_NAME, componentPortFilter );
addFilterToChain( connManager.getMultiplexerSocketAcceptor(), RawPrintFilter.FILTER_NAME, multiplexerPortFilter );
interpretedPrinter = new InterpretedXMLPrinter();
if (JiveGlobals.getBooleanProperty("plugin.debugger.interpretedAllowed")) {
// Add the packet interceptor that prints interpreted XML
InterceptorManager.getInstance().addInterceptor(interpretedPrinter);
}
// Listen to property events
PropertyEventDispatcher.addListener(this);
}
public void destroyPlugin() {
// Stop listening to property events
PropertyEventDispatcher.removeListener(this);
// Remove filter from filter chain builder
ConnectionManagerImpl connManager = (ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager();
removeFilterFromChain( connManager.getSocketAcceptor(), RawPrintFilter.FILTER_NAME );
removeFilterFromChain( connManager.getSSLSocketAcceptor(), RawPrintFilter.FILTER_NAME );
removeFilterFromChain( connManager.getComponentAcceptor(), RawPrintFilter.FILTER_NAME );
removeFilterFromChain( connManager.getMultiplexerSocketAcceptor(), RawPrintFilter.FILTER_NAME );
// Remove the filters from existing sessions
if (defaultPortFilter != null) {
defaultPortFilter.shutdown();
}
if (oldPortFilter != null) {
oldPortFilter.shutdown();
}
if (componentPortFilter != null) {
componentPortFilter.shutdown();
}
if (multiplexerPortFilter != null) {
multiplexerPortFilter.shutdown();
}
// Remove the packet interceptor that prints interpreted XML
InterceptorManager.getInstance().removeInterceptor(interpretedPrinter);
defaultPortFilter = null;
oldPortFilter = null;
componentPortFilter = null;
interpretedPrinter = null;
multiplexerPortFilter = null;
}
public RawPrintFilter getDefaultPortFilter() {
return defaultPortFilter;
}
public RawPrintFilter getOldPortFilter() {
return oldPortFilter;
}
public RawPrintFilter getComponentPortFilter() {
return componentPortFilter;
}
public RawPrintFilter getMultiplexerPortFilter() {
return multiplexerPortFilter;
}
public void propertySet(String property, Map<String, Object> params) {
if (property.equals("plugin.debugger.interpretedAllowed")) {
if (Boolean.parseBoolean((String) params.get("value"))) {
InterceptorManager.getInstance().addInterceptor(interpretedPrinter);
}
else {
InterceptorManager.getInstance().removeInterceptor(interpretedPrinter);
}
}
}
public void propertyDeleted(String property, Map<String, Object> params) {
if (property.equals("plugin.debugger.interpretedAllowed")) {
InterceptorManager.getInstance().removeInterceptor(interpretedPrinter);
}
}
public void xmlPropertySet(String property, Map<String, Object> params) {
// Do nothing
}
public void xmlPropertyDeleted(String property, Map<String, Object> params) {
// Do nothing
}
}