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.servicemix.jbi.management;
018    
019    import java.io.IOException;
020    import java.util.Collection;
021    import java.util.Iterator;
022    import java.util.LinkedHashMap;
023    import java.util.Map;
024    import java.util.concurrent.ConcurrentHashMap;
025    import java.util.concurrent.ExecutorService;
026    import java.util.concurrent.Executors;
027    
028    import javax.jbi.JBIException;
029    import javax.management.JMException;
030    import javax.management.MBeanAttributeInfo;
031    import javax.management.MBeanOperationInfo;
032    import javax.management.MBeanServer;
033    import javax.management.MalformedObjectNameException;
034    import javax.management.ObjectName;
035    
036    import org.apache.commons.logging.Log;
037    import org.apache.commons.logging.LogFactory;
038    import org.apache.servicemix.jbi.container.EnvironmentContext;
039    import org.apache.servicemix.jbi.container.JBIContainer;
040    import org.apache.servicemix.jbi.framework.ComponentMBeanImpl;
041    
042    /**
043     * Management Context applied to a ServiceMix container
044     * 
045     * @version $Revision: 665810 $
046     */
047    public class ManagementContext extends BaseSystemService implements ManagementContextMBean {
048        /**
049         * Default servicemix domain
050         */
051        public static final String DEFAULT_DOMAIN = "org.apache.servicemix";
052    
053        public static final String DEFAULT_CONNECTOR_PATH = "/jmxrmi";
054    
055        public static final int DEFAULT_CONNECTOR_PORT = 1099;
056    
057        private static final Log LOG = LogFactory.getLog(ManagementContext.class);
058    
059        protected Map<String, ObjectName> systemServices = new ConcurrentHashMap<String, ObjectName>();
060    
061        private Map<ObjectName, Object> beanMap = new ConcurrentHashMap<ObjectName, Object>();
062    
063        private MBeanServerContext mbeanServerContext = new MBeanServerContext();
064    
065        private ExecutorService executors;
066    
067        /**
068         * Default Constructor
069         */
070        public ManagementContext() {
071            mbeanServerContext.setJmxDomainName(DEFAULT_DOMAIN);
072        }
073    
074        /**
075         * Get the Description of the item
076         * 
077         * @return the description
078         */
079        public String getDescription() {
080            return "JMX Management";
081        }
082    
083        /**
084         * Get the MBeanServer
085         * 
086         * @return the MBeanServer
087         */
088        public MBeanServer getMBeanServer() {
089            return mbeanServerContext.getMBeanServer();
090        }
091    
092        /**
093         * @return the domain
094         */
095        public String getJmxDomainName() {
096            return mbeanServerContext.getJmxDomainName();
097        }
098    
099        /**
100         * @return Returns the useMBeanServer.
101         */
102        public boolean isUseMBeanServer() {
103            return mbeanServerContext.isUseMBeanServer();
104        }
105    
106        /**
107         * @param useMBeanServer
108         *            The useMBeanServer to set.
109         */
110        public void setUseMBeanServer(boolean useMBeanServer) {
111            mbeanServerContext.setUseMBeanServer(useMBeanServer);
112        }
113    
114        /**
115         * @return Returns the createMBeanServer flag.
116         */
117        public boolean isCreateMBeanServer() {
118            return mbeanServerContext.isCreateMBeanServer();
119        }
120    
121        /**
122         * @param enableJMX
123         *            Set createMBeanServer.
124         */
125        public void setCreateMBeanServer(boolean enableJMX) {
126            mbeanServerContext.setCreateMBeanServer(enableJMX);
127        }
128    
129        public void setNamingPort(int portNum) {
130            mbeanServerContext.setConnectorPort(portNum);
131        }
132    
133        public int getNamingPort() {
134            return mbeanServerContext.getConnectorPort();
135        }
136    
137        public boolean isCreateJmxConnector() {
138            return mbeanServerContext.isCreateConnector();
139        }
140    
141        public void setCreateJmxConnector(boolean createJmxConnector) {
142            mbeanServerContext.setCreateConnector(createJmxConnector);
143        }
144    
145        /**
146         * Initialize the ManagementContext
147         * 
148         * @param container
149         * @param server
150         * @throws JBIException
151         * 
152         */
153        public void init(JBIContainer container, MBeanServer server) throws JBIException {
154            if (container.isEmbedded() && server == null) {
155                mbeanServerContext.setUseMBeanServer(false);
156                mbeanServerContext.setCreateMBeanServer(false);
157                mbeanServerContext.setCreateConnector(false);
158            }
159            mbeanServerContext.setMBeanServer(server);
160            try {
161                mbeanServerContext.start();
162            } catch (IOException e) {
163                LOG.error("Failed to start mbeanServerContext", e);
164            }
165            this.executors = Executors.newCachedThreadPool();
166            super.init(container);
167        }
168    
169        protected Class<ManagementContextMBean> getServiceMBean() {
170            return ManagementContextMBean.class;
171        }
172    
173        /**
174         * Start the item.
175         * 
176         * @exception JBIException
177         *                if the item fails to start.
178         */
179        public void start() throws JBIException {
180            super.start();
181        }
182    
183        /**
184         * Stop the item. This suspends current messaging activities.
185         * 
186         * @exception JBIException
187         *                if the item fails to stop.
188         */
189        public void stop() throws JBIException {
190            super.stop();
191        }
192    
193        /**
194         * Shut down the item. The releases resources, preparatory to
195         * uninstallation.
196         * 
197         * @exception JBIException
198         *                if the item fails to shut down.
199         */
200        public void shutDown() throws JBIException {
201            super.shutDown();
202            // Unregister all mbeans
203            ObjectName[] beans = beanMap.keySet().toArray(new ObjectName[beanMap.size()]);
204            for (int i = 0; i < beans.length; i++) {
205                try {
206                    unregisterMBean(beans[i]);
207                } catch (Exception e) {
208                    LOG.debug("Could not unregister mbean", e);
209                }
210            }
211            try {
212                mbeanServerContext.stop();
213            } catch (IOException e) {
214                LOG.debug("Failed to shutdown mbeanServerContext cleanly", e);
215            }
216            executors.shutdown();
217        }
218    
219        /**
220         * Get a list of all binding components currently installed.
221         * 
222         * @return array of JMX object names of all installed BCs.
223         */
224        public ObjectName[] getBindingComponents() {
225            return container.getRegistry().getBindingComponents();
226        }
227    
228        /**
229         * Lookup a JBI Installable Component by its unique name.
230         * 
231         * @param componentName -
232         *            is the name of the BC or SE.
233         * @return the JMX object name of the component's LifeCycle MBean or null.
234         */
235        public ObjectName getComponentByName(String componentName) {
236            ComponentMBeanImpl component = container.getRegistry().getComponent(componentName);
237            return component != null ? component.getMBeanName() : null;
238        }
239    
240        /**
241         * Get a list of all engines currently installed.
242         * 
243         * @return array of JMX object names of all installed SEs.
244         */
245        public ObjectName[] getEngineComponents() {
246            return container.getRegistry().getEngineComponents();
247        }
248    
249        /**
250         * @return an array of ObjectNames for all Pojo components
251         */
252        public ObjectName[] getPojoComponents() {
253            return container.getRegistry().getPojoComponents();
254        }
255    
256        /**
257         * Return current version and other info about this JBI Framework.
258         * 
259         * @return info String
260         */
261        public String getSystemInfo() {
262            return "ServiceMix JBI Container: version: " + EnvironmentContext.getVersion();
263        }
264    
265        /**
266         * Lookup a system service by name.
267         * 
268         * @param serviceName -
269         *            is the name of the system service
270         * @return the JMX object name of the service or null
271         */
272        public ObjectName getSystemService(String serviceName) {
273            return systemServices.get(serviceName);
274        }
275    
276        /**
277         * Looks up all JBI Framework System Services currently installed.
278         * 
279         * @return array of JMX object names of system services
280         */
281        public ObjectName[] getSystemServices() {
282            ObjectName[] result = null;
283            Collection<ObjectName> col = systemServices.values();
284            result = new ObjectName[col.size()];
285            col.toArray(result);
286            return result;
287        }
288    
289        /**
290         * Check if a given JBI Installable Component is a Binding Component.
291         * 
292         * @param componentName -
293         *            the unique name of the component
294         * @return true if the component is a binding
295         */
296        public boolean isBinding(String componentName) {
297            ComponentMBeanImpl component = container.getRegistry().getComponent(componentName);
298            return component != null ? component.isBinding() : false;
299        }
300    
301        /**
302         * Check if a given JBI Component is a service engine.
303         * 
304         * @param componentName -
305         *            the unique name of the component
306         * @return true if the component is a service engine
307         */
308        public boolean isEngine(String componentName) {
309            ComponentMBeanImpl component = container.getRegistry().getComponent(componentName);
310            return component != null ? component.isEngine() : false;
311        }
312    
313        /**
314         * Start a Component
315         * 
316         * @param componentName
317         * @return the status
318         * @throws JBIException
319         */
320        public String startComponent(String componentName) throws JBIException {
321            String result = "NOT FOUND: " + componentName;
322            ObjectName objName = getComponentByName(componentName);
323            if (objName != null) {
324                ComponentMBeanImpl mbean = (ComponentMBeanImpl) beanMap.get(objName);
325                if (mbean != null) {
326                    mbean.start();
327                    result = mbean.getCurrentState();
328                }
329            }
330            return result;
331        }
332    
333        /**
334         * Stop a Component
335         * 
336         * @param componentName
337         * @return the status
338         * @throws JBIException
339         */
340        public String stopComponent(String componentName) throws JBIException {
341            String result = "NOT FOUND: " + componentName;
342            ObjectName objName = getComponentByName(componentName);
343            if (objName != null) {
344                ComponentMBeanImpl mbean = (ComponentMBeanImpl) beanMap.get(objName);
345                if (mbean != null) {
346                    mbean.stop();
347                    result = mbean.getCurrentState();
348                }
349            }
350            return result;
351        }
352    
353        /**
354         * Shutdown a Component
355         * 
356         * @param componentName
357         * @return the status
358         * @throws JBIException
359         */
360        public String shutDownComponent(String componentName) throws JBIException {
361            String result = "NOT FOUND: " + componentName;
362            ObjectName objName = getComponentByName(componentName);
363            if (objName != null) {
364                ComponentMBeanImpl mbean = (ComponentMBeanImpl) beanMap.get(objName);
365                if (mbean != null) {
366                    mbean.shutDown();
367                    result = mbean.getCurrentState();
368                }
369            }
370            return result;
371        }
372    
373        /**
374         * Formulate and return the MBean ObjectName of a custom control MBean for a
375         * JBI component.
376         * 
377         * @param type
378         * @param name
379         * @return the JMX ObjectName of the MBean, or <code>null</code> if
380         *         <code>customName</code> is invalid.
381         */
382        public ObjectName createCustomComponentMBeanName(String type, String name) {
383            Map<String, String> result = new LinkedHashMap<String, String>();
384            result.put("ContainerName", container.getName());
385            result.put("Type", "Component");
386            result.put("Name", sanitizeString(name));
387            result.put("SubType", sanitizeString(type));
388            return createObjectName(result);
389        }
390    
391        /**
392         * Create an ObjectName
393         * 
394         * @param provider
395         * @return the ObjectName
396         */
397        public ObjectName createObjectName(MBeanInfoProvider provider) {
398            Map<String, String> props = createObjectNameProps(provider);
399            return createObjectName(props);
400        }
401    
402        /**
403         * Create an ObjectName
404         * 
405         * @param name
406         * 
407         * @return the ObjectName
408         */
409        public ObjectName createObjectName(String name) {
410            ObjectName result = null;
411            try {
412                result = new ObjectName(name);
413            } catch (MalformedObjectNameException e) {
414                // shouldn't happen
415                String error = "Could not create ObjectName for " + name;
416                LOG.error(error, e);
417                throw new RuntimeException(error);
418            }
419            return result;
420        }
421    
422        /**
423         * Create an ObjectName
424         * 
425         * @param domain
426         * 
427         * @return the ObjectName
428         */
429        public ObjectName createObjectName(String domain, Map<String, String> props) {
430            StringBuffer sb = new StringBuffer();
431            sb.append(domain).append(':');
432            int i = 0;
433            for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
434                Map.Entry entry = (Map.Entry) it.next();
435                if (i++ > 0) {
436                    sb.append(",");
437                }
438                sb.append(entry.getKey()).append("=").append(entry.getValue());
439            }
440            ObjectName result = null;
441            try {
442                result = new ObjectName(sb.toString());
443            } catch (MalformedObjectNameException e) {
444                // shouldn't happen
445                String error = "Could not create ObjectName for " + props;
446                LOG.error(error, e);
447                throw new RuntimeException(error);
448            }
449            return result;
450        }
451    
452        /**
453         * Create an ObjectName
454         * 
455         * @param props
456         * @return the ObjectName
457         */
458        public ObjectName createObjectName(Map<String, String> props) {
459            return createObjectName(getJmxDomainName(), props);
460        }
461    
462        /**
463         * Create a String used to create an ObjectName
464         * 
465         * @param provider
466         * @return the ObjectName
467         */
468        public Map<String, String> createObjectNameProps(MBeanInfoProvider provider) {
469            return createObjectNameProps(provider, false);
470        }
471    
472        /**
473         * Create a String used to create an ObjectName
474         * 
475         * @param provider
476         * @return the ObjectName
477         */
478        public Map<String, String> createObjectNameProps(MBeanInfoProvider provider, boolean subTypeBeforeName) {
479            Map<String, String> result = new LinkedHashMap<String, String>();
480            result.put("ContainerName", container.getName());
481            result.put("Type", sanitizeString(provider.getType()));
482            if (subTypeBeforeName && provider.getSubType() != null) {
483                result.put("SubType", sanitizeString(provider.getSubType()));
484            }
485            result.put("Name", sanitizeString(provider.getName()));
486            if (!subTypeBeforeName && provider.getSubType() != null) {
487                result.put("SubType", sanitizeString(provider.getSubType()));
488            }
489            return result;
490        }
491    
492        /**
493         * The ':' and '/' characters are reserved in ObjectNames
494         * 
495         * @param in
496         * @return sanitized String
497         */
498        private static String sanitizeString(String in) {
499            String result = null;
500            if (in != null) {
501                result = in.replace(':', '_');
502                result = result.replace('/', '_');
503                result = result.replace('\\', '_');
504                result = result.replace('?', '_');
505                result = result.replace('=', '_');
506                result = result.replace(',', '_');
507            }
508            return result;
509        }
510    
511        /**
512         * Register an MBean
513         * 
514         * @param resource
515         * @param name
516         * @param interfaceMBean
517         * @throws JMException
518         */
519        public void registerMBean(ObjectName name, MBeanInfoProvider resource, Class interfaceMBean) throws JMException {
520            registerMBean(name, resource, interfaceMBean, resource.getDescription());
521        }
522    
523        /**
524         * Register an MBean
525         * 
526         * @param resource
527         * @param name
528         * @param interfaceMBean
529         * @param description
530         * @throws JMException
531         */
532        public void registerMBean(ObjectName name, Object resource, Class interfaceMBean, String description) throws JMException {
533            if (mbeanServerContext.getMBeanServer() != null) {
534                Object mbean = MBeanBuilder.buildStandardMBean(resource, interfaceMBean, description, executors);
535                if (mbeanServerContext.getMBeanServer().isRegistered(name)) {
536                    mbeanServerContext.getMBeanServer().unregisterMBean(name);
537                }
538                mbeanServerContext.getMBeanServer().registerMBean(mbean, name);
539                beanMap.put(name, resource);
540            }
541        }
542    
543        /**
544         * Retrive an System ObjectName
545         * 
546         * @param domainName
547         * @param containerName
548         * @param interfaceType
549         * @return the ObjectName
550         */
551        public static ObjectName getSystemObjectName(String domainName, String containerName, Class interfaceType) {
552            String tmp = domainName + ":ContainerName=" + containerName + ",Type=SystemService,Name=" + getSystemServiceName(interfaceType);
553            ObjectName result = null;
554            try {
555                result = new ObjectName(tmp);
556            } catch (MalformedObjectNameException e) {
557                LOG.error("Failed to build ObjectName:", e);
558            } catch (NullPointerException e) {
559                LOG.error("Failed to build ObjectName:", e);
560            }
561            return result;
562        }
563    
564        public static String getSystemServiceName(Class interfaceType) {
565            String name = interfaceType.getName();
566            name = name.substring(name.lastIndexOf('.') + 1);
567            if (name.endsWith("MBean")) {
568                name = name.substring(0, name.length() - 5);
569            }
570            return name;
571        }
572    
573        public static ObjectName getContainerObjectName(String domainName, String containerName) {
574            String tmp = domainName + ":ContainerName=" + containerName + ",Type=JBIContainer";
575            ObjectName result = null;
576            try {
577                result = new ObjectName(tmp);
578            } catch (MalformedObjectNameException e) {
579                LOG.debug("Unable to build ObjectName", e);
580            } catch (NullPointerException e) {
581                LOG.debug("Unable to build ObjectName", e);
582            }
583            return result;
584        }
585    
586        /**
587         * Register a System service
588         * 
589         * @param service
590         * @param interfaceType
591         * @throws JBIException
592         */
593        public void registerSystemService(BaseSystemService service, Class interfaceType) throws JBIException {
594            try {
595    
596                String name = service.getName();
597                if (systemServices.containsKey(name)) {
598                    throw new JBIException("A system service for the name " + name + " is already registered");
599                }
600                ObjectName objName = createObjectName(service);
601                if (LOG.isDebugEnabled()) {
602                    LOG.debug("Registering system service: " + objName);
603                }
604                registerMBean(objName, service, interfaceType, service.getDescription());
605                systemServices.put(name, objName);
606            } catch (MalformedObjectNameException e) {
607                throw new JBIException(e);
608            } catch (JMException e) {
609                throw new JBIException(e);
610            }
611        }
612    
613        /**
614         * Unregister a System service
615         * 
616         * @param service
617         * @throws JBIException
618         */
619        public void unregisterSystemService(BaseSystemService service) throws JBIException {
620            String name = service.getName();
621            if (!systemServices.containsKey(name)) {
622                throw new JBIException("A system service for the name " + name + " is not registered");
623            }
624            ObjectName objName = systemServices.remove(name);
625            if (LOG.isDebugEnabled()) {
626                LOG.debug("Unregistering system service: " + objName);
627            }
628            unregisterMBean(objName);
629        }
630    
631        /**
632         * Unregister an MBean
633         * 
634         * @param name
635         * @throws JBIException
636         */
637        public void unregisterMBean(ObjectName name) throws JBIException {
638            try {
639                mbeanServerContext.unregisterMBean(name);
640                beanMap.remove(name);
641            } catch (JMException e) {
642                LOG.error("Failed to unregister mbean: " + name, e);
643                throw new JBIException(e);
644            }
645        }
646    
647        /**
648         * Unregister an MBean
649         * 
650         * @param bean
651         * @throws JBIException
652         */
653        public void unregisterMBean(Object bean) throws JBIException {
654            for (Iterator i = beanMap.entrySet().iterator(); i.hasNext();) {
655                Map.Entry entry = (Map.Entry) i.next();
656                if (entry.getValue() == bean) {
657                    ObjectName name = (ObjectName) entry.getKey();
658                    unregisterMBean(name);
659                    break;
660                }
661            }
662        }
663    
664        /**
665         * Get an array of MBeanOperationInfo
666         * 
667         * @return array of OperationInfos
668         * @throws JMException
669         */
670        public MBeanAttributeInfo[] getAttributeInfos() throws JMException {
671            AttributeInfoHelper helper = new AttributeInfoHelper();
672            helper.addAttribute(getObjectToManage(), "bindingComponents", "Get list of all binding components");
673            helper.addAttribute(getObjectToManage(), "engineComponents", "Get list of all engine components");
674            helper.addAttribute(getObjectToManage(), "pojoComponents", "Get list of all pojo components");
675            helper.addAttribute(getObjectToManage(), "systemInfo", "Return current version");
676            helper.addAttribute(getObjectToManage(), "systemServices", "Get list of system services");
677            return AttributeInfoHelper.join(super.getAttributeInfos(), helper.getAttributeInfos());
678        }
679    
680        public MBeanOperationInfo[] getOperationInfos() throws JMException {
681            OperationInfoHelper helper = new OperationInfoHelper();
682            ParameterHelper ph = helper.addOperation(getObjectToManage(), "getComponentByName", 1, "look up Component by name");
683            ph.setDescription(0, "name", "Component name");
684            ph = helper.addOperation(getObjectToManage(), "getSystemService", 1, "look up System service by name");
685            ph.setDescription(0, "name", "System name");
686            ph = helper.addOperation(getObjectToManage(), "isBinding", 1, "Is Component a binding Component?");
687            ph.setDescription(0, "name", "Component name");
688            ph = helper.addOperation(getObjectToManage(), "isEngine", 1, "Is Component a service engine?");
689            ph.setDescription(0, "name", "Component name");
690            return OperationInfoHelper.join(super.getOperationInfos(), helper.getOperationInfos());
691        }
692    
693    }