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