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.geronimo.jmxremoting;
018    
019    import java.net.InetSocketAddress;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import javax.management.MBeanServer;
024    import javax.management.NotificationFilterSupport;
025    import javax.management.remote.JMXConnectionNotification;
026    import javax.management.remote.JMXConnectorServer;
027    import javax.management.remote.JMXConnectorServerFactory;
028    import javax.management.remote.JMXServiceURL;
029    
030    import org.apache.commons.logging.Log;
031    import org.apache.commons.logging.LogFactory;
032    import org.apache.geronimo.gbean.GBeanInfo;
033    import org.apache.geronimo.gbean.GBeanInfoBuilder;
034    import org.apache.geronimo.gbean.GBeanLifecycle;
035    import org.apache.geronimo.system.jmx.MBeanServerReference;
036    
037    /**
038     * A Connector that supports the server sideof JSR 160 JMX Remoting.
039     *
040     * @version $Rev: 555111 $ $Date: 2007-07-10 19:25:31 -0400 (Tue, 10 Jul 2007) $
041     */
042    public class JMXConnector implements JMXConnectorInfo, GBeanLifecycle {
043        private final MBeanServer mbeanServer;
044        private final Log log;
045        private final ClassLoader classLoader;
046        private String applicationConfigName;
047        private Authenticator authenticator;
048    
049        private String protocol;
050        private String host;
051        private int port = -1;
052        private String urlPath;
053    
054        private JMXConnectorServer server;
055        private JMXServiceURL jmxServiceURL;
056    
057        // todo remove this as soon as Geronimo supports factory beans
058        public JMXConnector(MBeanServerReference mbeanServerReference, String objectName, ClassLoader classLoader) {
059            this(mbeanServerReference.getMBeanServer(), objectName, classLoader);
060        }
061    
062        /**
063         * Constructor for creating the connector. The ClassLoader must be
064         * able to load all the LoginModules used in the JAAS login
065         *
066         * @param mbeanServer the mbean server
067         * @param objectName this connector's object name
068         * @param classLoader the classLoader used to create this connector
069         */
070        public JMXConnector(MBeanServer mbeanServer, String objectName, ClassLoader classLoader) {
071            this.mbeanServer = mbeanServer;
072            this.classLoader = classLoader;
073            log = LogFactory.getLog(objectName);
074        }
075    
076        /**
077         * Return the name of the JAAS Application Configuration Entry this
078         * connector uses to authenticate users. If null, users are not
079         * be authenticated (not recommended).
080         *
081         * @return the authentication configuration name
082         */
083        public String getApplicationConfigName() {
084            return applicationConfigName;
085        }
086    
087        /**
088         * Set the name of the JAAS Application Configuration Entry this
089         * connector should use to authenticate users. If null, users will not
090         * be authenticated (not recommended).
091         *
092         * @param applicationConfigName the authentication configuration name
093         */
094        public void setApplicationConfigName(String applicationConfigName) {
095            this.applicationConfigName = applicationConfigName;
096        }
097    
098        /**
099         * Every connector must specify a property of type InetSocketAddress
100         * because we use that to identify the network services to print a list
101         * during startup.  However, this can be read-only since the host and port
102         * are set in the url attribute.
103         */
104        public InetSocketAddress getListenAddress() {
105            return new InetSocketAddress(getHost(), getPort());
106        }
107    
108        /**
109         * Gets the protocol to use for the connection.
110         * @return the protocol to use for the connection
111         */
112        public String getProtocol() {
113            return protocol;
114        }
115    
116        /**
117         * Sets the protocol to use for the connection.
118         * @param protocol the protocol to use for the connection
119         */
120        public void setProtocol(String protocol) {
121            this.protocol = protocol;
122        }
123    
124        /**
125         * Gets the JMX host for this connector.
126         *
127         * @return the JMX host for this connector
128         */
129        public String getHost() {
130            return host;
131        }
132    
133        /**
134         * Sets the JMX host for this connector.
135         * @param host the JMX host for this connector
136         */
137        public void setHost(String host) {
138            this.host = host;
139        }
140    
141        /**
142         * Gets the JMX port for this connector.
143         *
144         * @return the JMX port for this connector
145         */
146        public int getPort() {
147            return port;
148        }
149    
150        /**
151         * Sets the JMX port for this connector.
152         * @param port the JMX port for this connector
153         */
154        public void setPort(int port) {
155            this.port = port;
156        }
157    
158        /**
159         * Gets the path within the target server to look for the connection.  This is commonly
160         * /jndi/rmi://localhost:1099/JMXConnector
161         * @return the path used to loacate the connector on the target server
162         */
163        public String getUrlPath() {
164            return urlPath;
165        }
166    
167        /**
168         * Sets the path within the target server to look for the connection.  This is commonly
169         * /jndi/rmi://localhost:1099/JMXConnector
170         * @param urlPath the path used to loacate the connector on the target server
171         */
172        public void setUrlPath(String urlPath) {
173            this.urlPath = urlPath;
174        }
175    
176        public void doStart() throws Exception {
177            jmxServiceURL = new JMXServiceURL(protocol, host, port, urlPath);
178            Map env = new HashMap();
179            if (applicationConfigName != null) {
180                authenticator = new Authenticator(applicationConfigName, classLoader);
181                env.put(JMXConnectorServer.AUTHENTICATOR, authenticator);
182            } else {
183                log.warn("Starting unauthenticating JMXConnector for " + jmxServiceURL);
184            }
185            server = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, env, mbeanServer);
186            NotificationFilterSupport filter = new NotificationFilterSupport();
187            filter.enableType(JMXConnectionNotification.OPENED);
188            filter.enableType(JMXConnectionNotification.CLOSED);
189            filter.enableType(JMXConnectionNotification.FAILED);
190            server.addNotificationListener(authenticator, filter, null);
191            server.start();
192            log.debug("Started JMXConnector " + server.getAddress());
193        }
194    
195        public void doStop() throws Exception {
196            try {
197                  server.stop();
198            } catch (java.io.IOException e) {
199                  // java.io.IOException is expected.
200            } catch (Exception e) {
201                  // Otherwise, something bad happened.  Rethrow the exception.
202                  throw e;
203            }
204            finally {
205              server = null;
206              log.debug("Stopped JMXConnector " + jmxServiceURL);
207            }
208        }
209    
210        public void doFail() {
211            try {
212                doStop();
213                log.warn("Failure in JMXConnector " + jmxServiceURL);
214            } catch (Exception e) {
215                log.warn("Error stopping JMXConnector after failure", e);
216            }
217        }
218    
219        public static final GBeanInfo GBEAN_INFO;
220    
221        static {
222            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("JMX Remoting Connector", JMXConnector.class);
223            infoFactory.addReference("MBeanServerReference", MBeanServerReference.class);
224            infoFactory.addAttribute("objectName", String.class, false);
225            infoFactory.addAttribute("classLoader", ClassLoader.class, false);
226    
227            infoFactory.addAttribute("protocol", String.class, true, true);
228            infoFactory.addAttribute("host", String.class, true, true);
229            infoFactory.addAttribute("port", int.class, true, true);
230            infoFactory.addAttribute("urlPath", String.class, true, true);
231            infoFactory.addAttribute("applicationConfigName", String.class, true, true);
232    
233            infoFactory.addInterface(JMXConnectorInfo.class);
234            
235            infoFactory.setConstructor(new String[]{"MBeanServerReference", "objectName", "classLoader"});
236            GBEAN_INFO = infoFactory.getBeanInfo();
237        }
238    
239        public static GBeanInfo getGBeanInfo() {
240            return GBEAN_INFO;
241        }
242    }