001// Copyright 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.hivemind.management.log4j;
016
017import java.util.ArrayList;
018import java.util.Enumeration;
019import java.util.List;
020
021import javax.management.Attribute;
022import javax.management.AttributeNotFoundException;
023import javax.management.InvalidAttributeValueException;
024import javax.management.MBeanAttributeInfo;
025import javax.management.MBeanConstructorInfo;
026import javax.management.MBeanException;
027import javax.management.MBeanInfo;
028import javax.management.MBeanNotificationInfo;
029import javax.management.MBeanOperationInfo;
030import javax.management.MBeanParameterInfo;
031import javax.management.Notification;
032import javax.management.NotificationListener;
033import javax.management.ObjectName;
034import javax.management.ReflectionException;
035import javax.management.RuntimeOperationsException;
036
037import org.apache.hivemind.management.mbeans.AbstractDynamicMBean;
038import org.apache.log4j.Appender;
039import org.apache.log4j.Level;
040import org.apache.log4j.Logger;
041import org.apache.log4j.helpers.OptionConverter;
042import org.apache.log4j.jmx.AppenderDynamicMBean;
043
044/**
045 * MBean for the management of a Log4j logger. Allows to change the level and add appenders. This is
046 * a copy of the {@link org.apache.log4j.jmx.LoggerDynamicMBean} from the log4 library. The copy was
047 * made to fix an issue with jboss 3.2.7, that don't accept spaces in attribute names. If somebody
048 * feels that such a copy from one apache project to another is not ok, please tell me.
049 * 
050 * @author Achim Huegen
051 */
052public class LoggerMBean extends AbstractDynamicMBean implements NotificationListener
053{
054
055    private MBeanConstructorInfo[] _constructors = new MBeanConstructorInfo[0];
056
057    private MBeanOperationInfo[] _operations = new MBeanOperationInfo[1];
058
059    private List _attributes = new ArrayList();
060
061    private String _className = this.getClass().getName();
062
063    private String _description = "This MBean acts as a management facade for a org.apache.log4j.Logger instance.";
064
065    // This Logger instance is for logging.
066    private static Logger _log = Logger.getLogger(LoggerMBean.class);
067
068    // We wrap this Logger instance.
069    private Logger _logger;
070
071    public LoggerMBean(Logger logger)
072    {
073        this._logger = logger;
074        buildDynamicMBeanInfo();
075    }
076
077    public void handleNotification(Notification notification, Object handback)
078    {
079        _log.debug("Received notification: " + notification.getType());
080        registerAppenderMBean((Appender) notification.getUserData());
081
082    }
083
084    private void buildDynamicMBeanInfo()
085    {
086        _attributes.add(new MBeanAttributeInfo("name", "java.lang.String",
087                "The name of this Logger.", true, false, false));
088
089        _attributes.add(new MBeanAttributeInfo("priority", "java.lang.String",
090                "The priority of this logger.", true, true, false));
091
092        MBeanParameterInfo[] params = new MBeanParameterInfo[2];
093        params[0] = new MBeanParameterInfo("class_name", "java.lang.String",
094                "add an appender to this logger");
095        params[1] = new MBeanParameterInfo("appender_name", "java.lang.String",
096                "name of the appender");
097
098        _operations[0] = new MBeanOperationInfo("addAppender", "addAppender(): add an appender",
099                params, "void", MBeanOperationInfo.ACTION);
100    }
101
102    protected Logger getLogger()
103    {
104        return _logger;
105    }
106
107    public MBeanInfo getMBeanInfo()
108    {
109        MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[_attributes.size()];
110        _attributes.toArray(attribs);
111
112        MBeanInfo mb = new MBeanInfo(_className, _description, attribs, _constructors, _operations,
113                new MBeanNotificationInfo[0]);
114        // cat.debug("getMBeanInfo exit.");
115        return mb;
116    }
117
118    public Object invoke(String operationName, Object params[], String signature[])
119            throws MBeanException, ReflectionException
120    {
121
122        if (operationName.equals("addAppender"))
123        {
124            addAppender((String) params[0], (String) params[1]);
125            return "Hello world.";
126        }
127
128        return null;
129    }
130
131    public Object getAttribute(String attributeName) throws AttributeNotFoundException,
132            MBeanException, ReflectionException
133    {
134
135        // Check attributeName is not null to avoid NullPointerException later on
136        if (attributeName == null)
137        {
138            throw new RuntimeOperationsException(new IllegalArgumentException(
139                    "Attribute name cannot be null"), "Cannot invoke a getter of " + _className
140                    + " with null attribute name");
141        }
142
143        // Check for a recognized attributeName and call the corresponding getter
144        if (attributeName.equals("name"))
145        {
146            return _logger.getName();
147        }
148        else if (attributeName.equals("priority"))
149        {
150            Level l = _logger.getLevel();
151            if (l == null)
152                return null;
153
154            return l.toString();
155        }
156        else if (attributeName.startsWith("appender="))
157        {
158            try
159            {
160                return new ObjectName("log4j:" + attributeName);
161            }
162            catch (Exception e)
163            {
164                _log.error("Could not create ObjectName" + attributeName);
165            }
166        }
167
168        // If attributeName has not been recognized throw an AttributeNotFoundException
169        throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in "
170                + _className));
171
172    }
173
174    void addAppender(String appenderClass, String appenderName)
175    {
176        _log.debug("addAppender called with " + appenderClass + ", " + appenderName);
177        Appender appender = (Appender) OptionConverter.instantiateByClassName(
178                appenderClass,
179                org.apache.log4j.Appender.class,
180                null);
181        appender.setName(appenderName);
182        _logger.addAppender(appender);
183
184    }
185
186    public void setAttribute(Attribute attribute) throws AttributeNotFoundException,
187            InvalidAttributeValueException, MBeanException, ReflectionException
188    {
189
190        // Check attribute is not null to avoid NullPointerException later on
191        if (attribute == null)
192        {
193            throw new RuntimeOperationsException(new IllegalArgumentException(
194                    "Attribute cannot be null"), "Cannot invoke a setter of " + _className
195                    + " with null attribute");
196        }
197        String name = attribute.getName();
198        Object value = attribute.getValue();
199
200        if (name == null)
201        {
202            throw new RuntimeOperationsException(new IllegalArgumentException(
203                    "Attribute name cannot be null"), "Cannot invoke the setter of " + _className
204                    + " with null attribute name");
205        }
206
207        if (name.equals("priority"))
208        {
209            if (value instanceof String)
210            {
211                String s = (String) value;
212                Level p = _logger.getLevel();
213                if (s.equalsIgnoreCase("NULL"))
214                {
215                    p = null;
216                }
217                else
218                {
219                    p = OptionConverter.toLevel(s, p);
220                }
221                _logger.setLevel(p);
222            }
223        }
224        else
225        {
226            throw (new AttributeNotFoundException("Attribute " + name + " not found in "
227                    + this.getClass().getName()));
228        }
229    }
230
231    void appenderMBeanRegistration()
232    {
233        Enumeration enumeration = _logger.getAllAppenders();
234        while (enumeration.hasMoreElements())
235        {
236            Appender appender = (Appender) enumeration.nextElement();
237            registerAppenderMBean(appender);
238        }
239    }
240
241    /**
242     * Register a mbean for an appender.
243     * 
244     * @param appender
245     */
246    void registerAppenderMBean(Appender appender)
247    {
248        String name = appender.getName();
249        _log.debug("Adding AppenderMBean for appender named " + name);
250        ObjectName objectName = null;
251        try
252        {
253            objectName = new ObjectName("log4j", "appender", name);
254            // register appender as mbean if not already existing
255            if (!getMBeanServer().isRegistered(objectName))
256            {
257                AppenderDynamicMBean appenderMBean = new AppenderDynamicMBean(appender);
258                getMBeanServer().registerMBean(appenderMBean, objectName);
259
260                _attributes.add(new MBeanAttributeInfo("appender=" + name,
261                        "javax.management.ObjectName", "The " + name + " appender.", true, true,
262                        false));
263            }
264
265        }
266        catch (Exception e)
267        {
268            _log.error("Could not add appenderMBean for [" + name + "].", e);
269        }
270    }
271
272    public void postRegister(java.lang.Boolean registrationDone)
273    {
274        appenderMBeanRegistration();
275    }
276}