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    
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: 442872 $ $Date: 2006-09-12 23:55:19 -0700 (Tue, 12 Sep 2006) $
050     */
051    public class ServerProxy
052    {
053        private static final Log log = LogFactory.getLog(ServerProxy.class);
054    
055        private JMXServiceURL url;
056    
057        private Map environment;
058    
059        private MBeanServerConnection mbeanConnection;
060    
061        private Throwable lastError;
062    
063        public ServerProxy(final JMXServiceURL url, final Map environment) throws Exception {
064            init(url, environment);
065        }
066    
067        public ServerProxy(final String hostname, final int port, final String username, final String password) throws Exception {
068            //
069            // TODO: Check if this is the right way to build up the URI
070            //
071            
072            this("service:jmx:rmi://" + hostname + "/jndi/rmi://" + hostname + ":" + port + "/JMXConnector", username, password);
073        }
074    
075        public ServerProxy(final String url, final String username, final String password) throws Exception {
076            assert url != null;
077            assert username != null;
078            assert password != null;
079            
080            Map env = new HashMap();
081            env.put("jmx.remote.credentials", new String[] {username, password});
082    
083            init(new JMXServiceURL(url), env);
084        }
085    
086        private void init(final JMXServiceURL url, final Map environment) throws Exception {
087            assert url != null;
088            assert environment != null;
089            
090            this.url = url;
091            this.environment = new HashMap();
092            this.environment.put("jmx.remote.credentials", new String[] {"system", "manager"});
093    
094            log.debug("Initialized with URL: " + url + ", environment: " + environment);
095        }
096    
097        private MBeanServerConnection getConnection() throws IOException {
098            if (this.mbeanConnection == null) {
099                log.debug("Connecting to: " + url);
100                
101                JMXConnector connector = JMXConnectorFactory.connect(url, environment);
102                this.mbeanConnection = connector.getMBeanServerConnection();
103                
104                log.debug("Connected");
105            }
106    
107            return mbeanConnection;
108        }
109    
110        public boolean isFullyStarted() {
111            boolean fullyStarted = true;
112    
113            try {
114                AbstractNameQuery query = new AbstractNameQuery(PersistentConfigurationList.class.getName());
115                Set result = listGBeans(query);
116                Iterator iter = result.iterator();
117                while (iter.hasNext()) {
118                    AbstractName name = (AbstractName)iter.next();
119                    boolean started = getBooleanAttribute(name, "kernelFullyStarted");
120                    if (!started) {
121                        fullyStarted = false;
122                        break;
123                    }
124                }
125            }
126            catch (IOException e) {
127                log.debug("Connection failure; ignoring", e);
128                fullyStarted = false;
129                lastError = e;
130            }
131            catch (Exception e) {
132                log.debug("Unable to determine if the server is fully started", e);
133                fullyStarted = false;
134                lastError = e;
135            }
136            
137            return fullyStarted;
138        }
139    
140        public Throwable getLastError() {
141            return lastError;
142        }
143    
144        public void shutdown() {
145            try {
146                invoke("shutdown");
147            }
148            catch (Exception e) {
149                log.warn("Unable to shutdown the server", e);
150                lastError = e;
151            }
152        }
153    
154        //
155        // Kernel invocation helpers
156        //
157    
158        private Object invoke(final String operation, final Object[] args, final String[] signature) throws Exception {
159            assert operation != null;
160            assert args != null;
161            assert signature != null;
162    
163            return getConnection().invoke(Kernel.KERNEL, operation, args, signature);
164        }
165    
166        private Object invoke(final String operation, final Object[] args) throws Exception {
167            assert args != null;
168    
169            String[] signature = new String[args.length];
170            for (int i=0; i<args.length; i++) {
171                signature[i] = args[i].getClass().getName();
172            }
173    
174            return invoke(operation, args, signature);
175        }
176    
177        private Object invoke(final String operation) throws Exception {
178            return invoke(operation, new Object[0]);
179        }
180    
181        private Set listGBeans(final AbstractNameQuery query) throws Exception {
182            return (Set)invoke("listGBeans", new Object[] { query });
183        }
184    
185        private Object getAttribute(final AbstractName name, final String attribute) throws Exception {
186            assert name != null;
187            assert attribute != null;
188    
189            return invoke("getAttribute", new Object[] { name, attribute });
190        }
191    
192        private boolean getBooleanAttribute(final AbstractName name, final String attribute) throws Exception {
193            Object obj = getAttribute(name, attribute);
194            if (obj instanceof Boolean) {
195                return ((Boolean)obj).booleanValue();
196            }
197            else {
198                throw new RuntimeException("Attribute is not of type Boolean: " + attribute);
199            }
200        }
201    }