001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements. See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache license, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License. You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the license for the specific language governing permissions and
015     * limitations under the license.
016     */
017    package org.apache.logging.log4j.core.jmx;
018    
019    import java.io.IOException;
020    import java.util.List;
021    import java.util.concurrent.Executor;
022    import java.util.concurrent.atomic.AtomicLong;
023    
024    import javax.management.MBeanNotificationInfo;
025    import javax.management.Notification;
026    import javax.management.NotificationBroadcasterSupport;
027    import javax.management.ObjectName;
028    
029    import org.apache.logging.log4j.Level;
030    import org.apache.logging.log4j.status.StatusData;
031    import org.apache.logging.log4j.status.StatusListener;
032    import org.apache.logging.log4j.status.StatusLogger;
033    
034    /**
035     * Implementation of the {@code StatusLoggerAdminMBean} interface.
036     */
037    public class StatusLoggerAdmin extends NotificationBroadcasterSupport implements StatusListener, StatusLoggerAdminMBean {
038    
039        private final AtomicLong sequenceNo = new AtomicLong();
040        private final ObjectName objectName;
041        private final String contextName;
042        private Level level = Level.WARN;
043    
044        /**
045         * Constructs a new {@code StatusLoggerAdmin} with the {@code Executor} to
046         * be used for sending {@code Notification}s asynchronously to listeners.
047         * 
048         * @param contextName name of the LoggerContext under which to register this
049         *            StatusLoggerAdmin. Note that the StatusLogger may be
050         *            registered multiple times, once for each LoggerContext. In web
051         *            containers, each web application has its own LoggerContext and
052         *            by associating the StatusLogger with the LoggerContext, all
053         *            associated MBeans can be unloaded when the web application is
054         *            undeployed.
055         * @param executor used to send notifications asynchronously
056         */
057        public StatusLoggerAdmin(final String contextName, final Executor executor) {
058            super(executor, createNotificationInfo());
059            this.contextName = contextName;
060            try {
061                final String mbeanName = String.format(PATTERN, Server.escape(contextName));
062                objectName = new ObjectName(mbeanName);
063            } catch (final Exception e) {
064                throw new IllegalStateException(e);
065            }
066            removeListeners(contextName);
067            StatusLogger.getLogger().registerListener(this);
068        }
069    
070        /**
071         * Add listener to StatusLogger for this context, or replace it if it already exists.
072         *
073         * @param ctxName
074         */
075        private void removeListeners(final String ctxName) {
076            final StatusLogger logger = StatusLogger.getLogger();
077            final Iterable<StatusListener> listeners = logger.getListeners();
078            // Remove any StatusLoggerAdmin listeners already registered for this context
079            for (final StatusListener statusListener : listeners) {
080                if (statusListener instanceof StatusLoggerAdmin) {
081                    final StatusLoggerAdmin adminListener = (StatusLoggerAdmin) statusListener;
082                    if (ctxName != null && ctxName.equals(adminListener.contextName)) {
083                        logger.removeListener(adminListener);
084                    }
085                }
086            }
087        }
088    
089        private static MBeanNotificationInfo createNotificationInfo() {
090            final String[] notifTypes = new String[] {
091                    NOTIF_TYPE_DATA,
092                    NOTIF_TYPE_MESSAGE };
093            final String name = Notification.class.getName();
094            final String description = "StatusLogger has logged an event";
095            return new MBeanNotificationInfo(notifTypes, name, description);
096        }
097    
098        @Override
099        public String[] getStatusDataHistory() {
100            final List<StatusData> data = getStatusData();
101            final String[] result = new String[data.size()];
102            for (int i = 0; i < result.length; i++) {
103                result[i] = data.get(i).getFormattedStatus();
104            }
105            return result;
106        }
107    
108        @Override
109        public List<StatusData> getStatusData() {
110            return StatusLogger.getLogger().getStatusData();
111        }
112    
113        @Override
114        public String getLevel() {
115            return this.level.name();
116        }
117    
118        @Override
119        public Level getStatusLevel() {
120            return this.level;
121        }
122    
123        @Override
124        public void setLevel(final String level) {
125            this.level = Level.toLevel(level, Level.ERROR);
126        }
127    
128        @Override
129        public String getContextName() {
130            return contextName;
131        }
132    
133        /*
134         * (non-Javadoc)
135         * 
136         * @see
137         * org.apache.logging.log4j.status.StatusListener#log(org.apache.logging
138         * .log4j.status.StatusData)
139         */
140        @Override
141        public void log(final StatusData data) {
142            final Notification notifMsg = new Notification(NOTIF_TYPE_MESSAGE, getObjectName(), nextSeqNo(), nowMillis(),
143                    data.getFormattedStatus());
144            sendNotification(notifMsg);
145    
146            final Notification notifData = new Notification(NOTIF_TYPE_DATA, getObjectName(), nextSeqNo(), nowMillis());
147            notifData.setUserData(data);
148            sendNotification(notifData);
149        }
150    
151        /**
152         * Returns the {@code ObjectName} of this mbean.
153         * 
154         * @return the {@code ObjectName}
155         * @see StatusLoggerAdminMBean#PATTERN
156         */
157        @Override
158        public ObjectName getObjectName() {
159            return objectName;
160        }
161    
162        private long nextSeqNo() {
163            return sequenceNo.getAndIncrement();
164        }
165    
166        private long nowMillis() {
167            return System.currentTimeMillis();
168        }
169    
170        @Override
171        public void close() throws IOException {
172            // nothing to close here
173        }
174    }