001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License.
018     */
019    
020    package org.apache.geronimo.mavenplugins.geronimo;
021    
022    import java.util.HashMap;
023    import java.util.Map;
024    import java.util.Set;
025    import java.util.Iterator;
026    import java.io.IOException;
027    
028    import javax.management.remote.JMXServiceURL;
029    import javax.management.remote.JMXConnector;
030    import javax.management.remote.JMXConnectorFactory;
031    import javax.management.MBeanServerConnection;
032    
033    //
034    // FIXME: It should be possible to query state with-out any Geronimo classes,
035    //        just using JMX interfaces.
036    //
037    
038    import org.apache.geronimo.gbean.AbstractName;
039    import org.apache.geronimo.gbean.AbstractNameQuery;
040    import org.apache.geronimo.kernel.Kernel;
041    import org.apache.geronimo.kernel.config.PersistentConfigurationList;
042    import org.apache.geronimo.system.serverinfo.ServerInfo;
043    import org.apache.commons.logging.Log;
044    import org.apache.commons.logging.LogFactory;
045    
046    /**
047     * Helper to communicate with a remote server via JMX.
048     *
049     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
050     */
051    public class ServerProxy
052    {
053        private static final Log log = LogFactory.getLog(ServerProxy.class);
054    
055        private JMXServiceURL url;
056    
057        private JMXConnector connector;
058        
059        private Map environment;
060    
061        private MBeanServerConnection mbeanConnection;
062    
063        private Throwable lastError;    
064    
065        public ServerProxy(final JMXServiceURL url, final Map environment) throws Exception {
066            assert url != null;
067            assert environment != null;
068    
069            this.url = url;
070            this.environment = environment;
071            
072            log.debug("Initialized with URL: " + url + ", environment: " + environment);
073        }
074    
075        public ServerProxy(final String hostname, final int port, final String username, final String password) throws Exception {
076            this("service:jmx:rmi://" + hostname + "/jndi/rmi://" + hostname + ":" + port + "/JMXConnector", username, password);
077        }
078    
079        public ServerProxy(final String url, final String username, final String password) throws Exception {
080            assert url != null;
081            assert username != null;
082            assert password != null;
083            
084            this.url = new JMXServiceURL(url);
085            this.environment = new HashMap();
086            this.environment.put("jmx.remote.credentials", new String[] {username, password});
087    
088            log.debug("Initialized with URL: " + url + ", environment: " + environment);
089        }
090    
091        private MBeanServerConnection getConnection() throws IOException {
092            if (this.mbeanConnection == null) {
093                log.debug("Connecting to: " + url);
094                
095                this.connector = JMXConnectorFactory.connect(url, environment);
096                this.mbeanConnection = this.connector.getMBeanServerConnection();
097                
098                log.debug("Connected");
099            }
100    
101            return mbeanConnection;
102        }
103    
104        public void closeConnection() {
105            if (this.connector != null) {
106                try {
107                    this.connector.close();
108                } catch (IOException e) {
109                    String msg = "Failed to close JMXConnector";
110                    if (log.isTraceEnabled()) {
111                        log.trace(msg,e);
112                    }
113                    if (log.isDebugEnabled()) {
114                        log.debug(msg + ":" + e);
115                    }
116                } finally {
117                    this.connector = null;
118                    this.mbeanConnection = null;
119                    this.lastError = null;
120                }
121            }
122        }
123        
124        public boolean isFullyStarted() {
125            boolean fullyStarted = true;
126    
127            try {
128                AbstractNameQuery query = new AbstractNameQuery(PersistentConfigurationList.class.getName());
129                Set result = listGBeans(query);
130                Iterator iter = result.iterator();
131                while (iter.hasNext()) {
132                    AbstractName name = (AbstractName)iter.next();
133                    boolean started = getBooleanAttribute(name, "kernelFullyStarted");
134                    if (!started) {
135                        fullyStarted = false;
136                        break;
137                    }
138                }
139            }
140            catch (IOException e) {
141                String msg = "Connection failure; ignoring";
142                if (log.isTraceEnabled()) {
143                    log.trace(msg, e);
144                }
145                else if (log.isDebugEnabled()) {
146                    log.debug(msg + ": " + e);
147                }
148                
149                fullyStarted = false;
150                lastError = e;
151            }
152            catch (Exception e) {
153                String msg = "Unable to determine if the server is fully started; ignoring";
154                if (log.isTraceEnabled()) {
155                    log.trace(msg, e);
156                }
157                else if (log.isDebugEnabled()) {
158                    log.debug(msg + ": " + e);
159                }
160                
161                fullyStarted = false;
162                lastError = e;
163            }
164            
165            return fullyStarted;
166        }
167    
168        public String getGeronimoHome() {
169            String home = null;
170    
171            try {
172                AbstractNameQuery query = new AbstractNameQuery(ServerInfo.class.getName());
173                Set result = listGBeans(query);
174                Iterator iter = result.iterator();
175                while (iter.hasNext()) {
176                    AbstractName name = (AbstractName)iter.next();
177                    home  = (String)getAttribute(name, "currentBaseDirectory");
178                    if (home != null) {
179                        break;
180                    }
181                }
182            }
183            catch (IOException e) {
184                String msg = "Connection failure; ignoring";
185                if (log.isTraceEnabled()) {
186                    log.trace(msg, e);
187                }
188                else if (log.isDebugEnabled()) {
189                    log.debug(msg + ": " + e);
190                }
191                
192                lastError = e;
193            }
194            catch (Exception e) {
195                String msg = "Unable to determine if the server is fully started; ignoring";
196                if (log.isTraceEnabled()) {
197                    log.trace(msg, e);
198                }
199                else if (log.isDebugEnabled()) {
200                    log.debug(msg + ": " + e);
201                }
202                
203                lastError = e;
204            }
205            
206            return home;
207        }
208    
209        public Throwable getLastError() {
210            return lastError;
211        }
212    
213        public void shutdown() {
214            try {
215                invoke("shutdown");
216            }
217            catch (Exception e) {
218                log.warn("Unable to shutdown the server", e);
219                lastError = e;
220            }
221        }
222    
223        //
224        // Kernel invocation helpers
225        //
226    
227        private Object invoke(final String operation, final Object[] args, final String[] signature) throws Exception {
228            assert operation != null;
229            assert args != null;
230            assert signature != null;
231    
232            return getConnection().invoke(Kernel.KERNEL, operation, args, signature);
233        }
234    
235        private Object invoke(final String operation, final Object[] args) throws Exception {
236            assert args != null;
237    
238            String[] signature = new String[args.length];
239            for (int i=0; i<args.length; i++) {
240                signature[i] = args[i].getClass().getName();
241            }
242    
243            return invoke(operation, args, signature);
244        }
245    
246        private Object invoke(final String operation) throws Exception {
247            return invoke(operation, new Object[0]);
248        }
249    
250        private Set listGBeans(final AbstractNameQuery query) throws Exception {
251            return (Set)invoke("listGBeans", new Object[] { query });
252        }
253    
254        private Object getAttribute(final AbstractName name, final String attribute) throws Exception {
255            assert name != null;
256            assert attribute != null;
257    
258            return invoke("getAttribute", new Object[] { name, attribute });
259        }
260    
261        private boolean getBooleanAttribute(final AbstractName name, final String attribute) throws Exception {
262            Object obj = getAttribute(name, attribute);
263            if (obj instanceof Boolean) {
264                return ((Boolean)obj).booleanValue();
265            }
266            else {
267                throw new RuntimeException("Attribute is not of type Boolean: " + attribute);
268            }
269        }
270    }