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. 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     */
052    public 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    }