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.activemq.transport;
018    
019    import org.apache.activemq.broker.jmx.AnnotatedMBean;
020    import org.apache.activemq.broker.jmx.ManagementContext;
021    import org.apache.activemq.util.IOExceptionSupport;
022    import org.apache.activemq.util.LogWriterFinder;
023    import org.slf4j.Logger;
024    import org.slf4j.LoggerFactory;
025    import java.io.IOException;
026    import javax.management.ObjectName;
027    
028    /**
029     * Singleton class to create TransportLogger objects.
030     * When the method getInstance() is called for the first time,
031     * a TransportLoggerControlMBean is created and registered.
032     * This MBean permits enabling and disabling the logging for
033     * all TransportLogger objects at once.
034     * 
035     * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com
036     * 
037     * @see TransportLoggerControlMBean
038     */
039    public class TransportLoggerFactory {
040    
041        private static final Logger LOG = LoggerFactory.getLogger(TransportLoggerFactory.class);
042    
043        private static TransportLoggerFactory instance;
044        private static int lastId=0;
045        private static final LogWriterFinder logWriterFinder = new LogWriterFinder("META-INF/services/org/apache/activemq/transport/logwriters/");
046    
047        /**
048         * LogWriter that will be used if none is specified.
049         */
050        public static String defaultLogWriterName = "default";
051        /**
052         * If transport logging is enabled, it will be possible to control
053         * the transport loggers or not based on this value 
054         */
055        private static boolean defaultDynamicManagement = false;
056        /**
057         * If transport logging is enabled, the transport loggers will initially
058         * output or not depending on this value.
059         * This setting only has a meaning if 
060         */
061        private static boolean defaultInitialBehavior = true;
062        /**
063         * Default port to control the transport loggers through JMX
064         */
065        private static int defaultJmxPort = 1099;
066    
067        private boolean transportLoggerControlCreated = false;
068        private ManagementContext managementContext;
069        private ObjectName objectName;
070    
071        /**
072         * Private constructor.
073         */
074        private TransportLoggerFactory() {
075        }
076    
077        /**
078         * Returns a TransportLoggerFactory object which can be used to create TransportLogger objects.
079         * @return a TransportLoggerFactory object
080         */
081        public static synchronized TransportLoggerFactory getInstance() {
082            if (instance == null) {
083                instance = new TransportLoggerFactory();
084            }
085            return instance;
086        }
087    
088        public void stop() {
089            try {
090                if (this.transportLoggerControlCreated) {
091                    this.managementContext.unregisterMBean(this.objectName);
092                    this.managementContext.stop();
093                    this.managementContext = null;
094                }
095            } catch (Exception e) {
096                LOG.error("TransportLoggerFactory could not be stopped, reason: " + e, e);
097            }
098    
099        }
100    
101        /**
102         * Creates a TransportLogger object, that will be inserted in the Transport Stack.
103         * Uses the default initial behavior, the default log writer, and creates a new
104         * log4j object to be used by the TransportLogger.
105         * @param next The next Transport layer in the Transport stack.
106         * @return A TransportLogger object.
107         * @throws IOException
108         */
109        public TransportLogger createTransportLogger(Transport next) throws IOException {
110            int id = getNextId();
111            return createTransportLogger(next, id, createLog(id), defaultLogWriterName, defaultDynamicManagement, defaultInitialBehavior, defaultJmxPort);
112        }
113        
114        /**
115         * Creates a TransportLogger object, that will be inserted in the Transport Stack.
116         * Uses the default initial behavior and the default log writer.
117         * @param next The next Transport layer in the Transport stack.
118         * @param log The log4j log that will be used by the TransportLogger.
119         * @return A TransportLogger object.
120         * @throws IOException
121         */
122        public TransportLogger createTransportLogger(Transport next, Logger log) throws IOException {
123            return createTransportLogger(next, getNextId(), log, defaultLogWriterName, defaultDynamicManagement, defaultInitialBehavior, defaultJmxPort);
124        }
125    
126        /**
127         * Creates a TransportLogger object, that will be inserted in the Transport Stack.
128         * Creates a new log4j object to be used by the TransportLogger.
129         * @param next The next Transport layer in the Transport stack.
130         * @param startLogging Specifies if this TransportLogger should be initially active or not.
131         * @param logWriterName The name or the LogWriter to be used. Different log writers can output
132         * logs with a different format.
133         * @return A TransportLogger object.
134         * @throws IOException
135         */
136        public TransportLogger createTransportLogger(Transport next, String logWriterName,
137                boolean useJmx, boolean startLogging, int jmxport) throws IOException {
138            int id = getNextId();
139            return createTransportLogger(next, id, createLog(id), logWriterName, useJmx, startLogging, jmxport);
140        }
141    
142    
143    
144        /**
145         * Creates a TransportLogger object, that will be inserted in the Transport Stack.
146         * @param next The next Transport layer in the Transport stack.
147         * @param id The id of the transport logger.
148         * @param log The log4j log that will be used by the TransportLogger.
149         * @param logWriterName The name or the LogWriter to be used. Different log writers can output
150         * @param dynamicManagement Specifies if JMX will be used to switch on/off the TransportLogger to be created.
151         * @param startLogging Specifies if this TransportLogger should be initially active or not. Only has a meaning if
152         * dynamicManagement = true.
153         * @param jmxPort the port to be used by the JMX server. It should only be different from 1099 (broker's default JMX port)
154         * when it's a client that is using Transport Logging. In a broker, if the port is different from 1099, 2 JMX servers will
155         * be created, both identical, with all the MBeans.
156         * @return A TransportLogger object.
157         * @throws IOException
158         */
159        public TransportLogger createTransportLogger(Transport next, int id, Logger log,
160                String logWriterName, boolean dynamicManagement, boolean startLogging, int jmxport) throws IOException {
161            try {
162                LogWriter logWriter = logWriterFinder.newInstance(logWriterName);
163                TransportLogger tl =  new TransportLogger (next, log, startLogging, logWriter);
164                if (dynamicManagement) {
165                    synchronized (this) {
166                        if (!this.transportLoggerControlCreated) {
167                            this.createTransportLoggerControl(jmxport);
168                        }
169                    }
170                    TransportLoggerView tlv = new TransportLoggerView(tl, next.toString(), id, this.managementContext);
171                    tl.setView(tlv);
172                }
173                return tl;
174            } catch (Throwable e) {
175                throw IOExceptionSupport.create("Could not create log writer object for: " + logWriterName + ", reason: " + e, e);
176            }
177        }
178    
179        synchronized private static int getNextId() {
180            return ++lastId;
181        }
182    
183        private static Logger createLog(int id) {
184            return LoggerFactory.getLogger(TransportLogger.class.getName()+".Connection:" + id);
185        }
186        
187        /**
188         * Starts the management context.
189         * Creates and registers a TransportLoggerControl MBean which enables the user
190         * to enable/disable logging for all transport loggers at once.
191         */
192         private void createTransportLoggerControl(int port) {
193             try {
194                 this.managementContext = new ManagementContext();
195                 this.managementContext.setConnectorPort(port);
196                 this.managementContext.start();
197             } catch (Exception e) {
198                 LOG.error("Management context could not be started, reason: " + e, e);
199             }
200    
201             try {
202                 this.objectName = new ObjectName(this.managementContext.getJmxDomainName()+":"+ "Type=TransportLoggerControl");
203                 AnnotatedMBean.registerMBean(this.managementContext, new TransportLoggerControl(this.managementContext),this.objectName);
204                 
205                 this.transportLoggerControlCreated = true;
206    
207             } catch (Exception e) {
208                 LOG.error("TransportLoggerControlMBean could not be registered, reason: " + e, e);
209             }
210         }
211    
212    }