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 */
017package org.apache.camel.management.mbean;
018
019import java.io.ByteArrayInputStream;
020import java.io.InputStream;
021import java.net.URLDecoder;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Comparator;
025import java.util.List;
026import java.util.Map;
027import java.util.Properties;
028import java.util.Set;
029import java.util.concurrent.TimeUnit;
030import java.util.concurrent.atomic.AtomicBoolean;
031import javax.management.MBeanServer;
032import javax.management.ObjectName;
033import javax.management.openmbean.CompositeData;
034import javax.management.openmbean.CompositeDataSupport;
035import javax.management.openmbean.CompositeType;
036import javax.management.openmbean.TabularData;
037import javax.management.openmbean.TabularDataSupport;
038
039import org.w3c.dom.Document;
040
041import org.apache.camel.CamelContext;
042import org.apache.camel.CatalogCamelContext;
043import org.apache.camel.Endpoint;
044import org.apache.camel.ManagementStatisticsLevel;
045import org.apache.camel.Producer;
046import org.apache.camel.ProducerTemplate;
047import org.apache.camel.Route;
048import org.apache.camel.RuntimeCamelException;
049import org.apache.camel.TimerListener;
050import org.apache.camel.api.management.ManagedResource;
051import org.apache.camel.api.management.mbean.CamelOpenMBeanTypes;
052import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
053import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
054import org.apache.camel.api.management.mbean.ManagedRouteMBean;
055import org.apache.camel.api.management.mbean.ManagedStepMBean;
056import org.apache.camel.model.ModelCamelContext;
057import org.apache.camel.model.ModelHelper;
058import org.apache.camel.model.RouteDefinition;
059import org.apache.camel.model.RoutesDefinition;
060import org.apache.camel.model.rest.RestDefinition;
061import org.apache.camel.model.rest.RestsDefinition;
062import org.apache.camel.spi.ManagementStrategy;
063import org.apache.camel.support.JSonSchemaHelper;
064import org.apache.camel.util.XmlLineNumberParser;
065import org.slf4j.Logger;
066import org.slf4j.LoggerFactory;
067
068@ManagedResource(description = "Managed CamelContext")
069public class ManagedCamelContext extends ManagedPerformanceCounter implements TimerListener, ManagedCamelContextMBean {
070
071    private static final Logger LOG = LoggerFactory.getLogger(ManagedCamelContext.class);
072
073    private final ModelCamelContext context;
074    private final LoadTriplet load = new LoadTriplet();
075    private final String jmxDomain;
076
077    public ManagedCamelContext(ModelCamelContext context) {
078        this.context = context;
079        this.jmxDomain = context.getManagementStrategy().getManagementAgent().getMBeanObjectDomainName();
080    }
081
082    @Override
083    public void init(ManagementStrategy strategy) {
084        super.init(strategy);
085        boolean enabled = context.getManagementStrategy().getManagementAgent() != null && context.getManagementStrategy().getManagementAgent().getStatisticsLevel() != ManagementStatisticsLevel.Off;
086        setStatisticsEnabled(enabled);
087    }
088
089    public CamelContext getContext() {
090        return context;
091    }
092
093    public String getCamelId() {
094        return context.getName();
095    }
096
097    public String getManagementName() {
098        return context.getManagementName();
099    }
100
101    public String getCamelVersion() {
102        return context.getVersion();
103    }
104
105    public String getState() {
106        return context.getStatus().name();
107    }
108
109    public String getUptime() {
110        return context.getUptime();
111    }
112
113    public long getUptimeMillis() {
114        return context.getUptimeMillis();
115    }
116
117    public String getManagementStatisticsLevel() {
118        if (context.getManagementStrategy().getManagementAgent() != null) {
119            return context.getManagementStrategy().getManagementAgent().getStatisticsLevel().name();
120        } else {
121            return null;
122        }
123    }
124
125    public String getClassResolver() {
126        return context.getClassResolver().getClass().getName();
127    }
128
129    public String getPackageScanClassResolver() {
130        return context.getPackageScanClassResolver().getClass().getName();
131    }
132
133    public String getApplicationContextClassName() {
134        if (context.getApplicationContextClassLoader() != null) {
135            return context.getApplicationContextClassLoader().getClass().getName();
136        } else {
137            return null;
138        }
139    }
140
141    @Override
142    public String getHeadersMapFactoryClassName() {
143        return context.getHeadersMapFactory().getClass().getName();
144    }
145
146    @Override
147    public Map<String, String> getGlobalOptions() {
148        if (context.getGlobalOptions().isEmpty()) {
149            return null;
150        }
151        return context.getGlobalOptions();
152    }
153
154    @Override
155    public String getGlobalOption(String key) throws Exception {
156        return context.getGlobalOption(key);
157    }
158
159    @Override
160    public void setGlobalOption(String key, String value) throws Exception {
161        context.getGlobalOptions().put(key, value);
162    }
163
164    public Boolean getTracing() {
165        return context.isTracing();
166    }
167
168    public void setTracing(Boolean tracing) {
169        context.setTracing(tracing);
170    }
171
172    public Integer getInflightExchanges() {
173        return (int) super.getExchangesInflight();
174    }
175
176    public Integer getTotalRoutes() {
177        return context.getRoutes().size();
178    }
179
180    public Integer getStartedRoutes() {
181        int started = 0;
182        for (Route route : context.getRoutes()) {
183            if (context.getRouteController().getRouteStatus(route.getId()).isStarted()) {
184                started++;
185            }
186        }
187        return started;
188    }
189
190    public void setTimeout(long timeout) {
191        context.getShutdownStrategy().setTimeout(timeout);
192    }
193
194    public long getTimeout() {
195        return context.getShutdownStrategy().getTimeout();
196    }
197
198    public void setTimeUnit(TimeUnit timeUnit) {
199        context.getShutdownStrategy().setTimeUnit(timeUnit);
200    }
201
202    public TimeUnit getTimeUnit() {
203        return context.getShutdownStrategy().getTimeUnit();
204    }
205
206    public void setShutdownNowOnTimeout(boolean shutdownNowOnTimeout) {
207        context.getShutdownStrategy().setShutdownNowOnTimeout(shutdownNowOnTimeout);
208    }
209
210    public boolean isShutdownNowOnTimeout() {
211        return context.getShutdownStrategy().isShutdownNowOnTimeout();
212    }
213
214    public String getLoad01() {
215        double load1 = load.getLoad1();
216        if (Double.isNaN(load1)) {
217            // empty string if load statistics is disabled
218            return "";
219        } else {
220            return String.format("%.2f", load1);
221        }
222    }
223
224    public String getLoad05() {
225        double load5 = load.getLoad5();
226        if (Double.isNaN(load5)) {
227            // empty string if load statistics is disabled
228            return "";
229        } else {
230            return String.format("%.2f", load5);
231        }
232    }
233
234    public String getLoad15() {
235        double load15 = load.getLoad15();
236        if (Double.isNaN(load15)) {
237            // empty string if load statistics is disabled
238            return "";
239        } else {
240            return String.format("%.2f", load15);
241        }
242    }
243
244    public boolean isUseBreadcrumb() {
245        return context.isUseBreadcrumb();
246    }
247
248    public boolean isAllowUseOriginalMessage() {
249        return context.isAllowUseOriginalMessage();
250    }
251
252    public boolean isMessageHistory() {
253        return context.isMessageHistory() != null ? context.isMessageHistory() : false;
254    }
255
256    public boolean isLogMask() {
257        return context.isLogMask() != null ? context.isLogMask() : false;
258    }
259
260    public boolean isUseMDCLogging() {
261        return context.isUseMDCLogging();
262    }
263
264    public boolean isUseDataType() {
265        return context.isUseDataType();
266    }
267
268    public void onTimer() {
269        load.update(getInflightExchanges());
270    }
271
272    public void start() throws Exception {
273        if (context.isSuspended()) {
274            context.resume();
275        } else {
276            context.start();
277        }
278    }
279
280    public void stop() throws Exception {
281        context.stop();
282    }
283
284    public void restart() throws Exception {
285        context.stop();
286        context.start();
287    }
288
289    public void suspend() throws Exception {
290        context.suspend();
291    }
292
293    public void resume() throws Exception {
294        if (context.isSuspended()) {
295            context.resume();
296        } else {
297            throw new IllegalStateException("CamelContext is not suspended");
298        }
299    }
300
301    public void startAllRoutes() throws Exception {
302        context.getRouteController().startAllRoutes();
303    }
304
305    public boolean canSendToEndpoint(String endpointUri) {
306        try {
307            Endpoint endpoint = context.getEndpoint(endpointUri);
308            if (endpoint != null) {
309                Producer producer = endpoint.createProducer();
310                return producer != null;
311            }
312        } catch (Exception e) {
313            // ignore
314        }
315
316        return false;
317    }
318
319    public void sendBody(String endpointUri, Object body) throws Exception {
320        ProducerTemplate template = context.createProducerTemplate();
321        try {
322            template.sendBody(endpointUri, body);
323        } finally {
324            template.stop();
325        }
326    }
327
328    public void sendStringBody(String endpointUri, String body) throws Exception {
329        sendBody(endpointUri, body);
330    }
331
332    public void sendBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception {
333        ProducerTemplate template = context.createProducerTemplate();
334        try {
335            template.sendBodyAndHeaders(endpointUri, body, headers);
336        } finally {
337            template.stop();
338        }
339    }
340
341    public Object requestBody(String endpointUri, Object body) throws Exception {
342        ProducerTemplate template = context.createProducerTemplate();
343        Object answer = null;
344        try {
345            answer = template.requestBody(endpointUri, body);
346        } finally {
347            template.stop();
348        }
349        return answer;
350    }
351
352    public Object requestStringBody(String endpointUri, String body) throws Exception {
353        return requestBody(endpointUri, body);
354    }
355
356    public Object requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception {
357        ProducerTemplate template = context.createProducerTemplate();
358        Object answer = null;
359        try {
360            answer = template.requestBodyAndHeaders(endpointUri, body, headers);
361        } finally {
362            template.stop();
363        }
364        return answer;
365    }
366
367    public String dumpRestsAsXml() throws Exception {
368        return dumpRestsAsXml(false);
369    }
370
371    @Override
372    public String dumpRestsAsXml(boolean resolvePlaceholders) throws Exception {
373        List<RestDefinition> rests = context.getRestDefinitions();
374        if (rests.isEmpty()) {
375            return null;
376        }
377
378        // use a routes definition to dump the rests
379        RestsDefinition def = new RestsDefinition();
380        def.setRests(rests);
381        String xml = ModelHelper.dumpModelAsXml(context, def);
382
383        // if resolving placeholders we parse the xml, and resolve the property placeholders during parsing
384        if (resolvePlaceholders) {
385            final AtomicBoolean changed = new AtomicBoolean();
386            InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));
387            Document dom = XmlLineNumberParser.parseXml(is, new XmlLineNumberParser.XmlTextTransformer() {
388                @Override
389                public String transform(String text) {
390                    try {
391                        String after = getContext().resolvePropertyPlaceholders(text);
392                        if (!changed.get()) {
393                            changed.set(!text.equals(after));
394                        }
395                        return after;
396                    } catch (Exception e) {
397                        // ignore
398                        return text;
399                    }
400                }
401            });
402            // okay there were some property placeholder replaced so re-create the model
403            if (changed.get()) {
404                xml = context.getTypeConverter().mandatoryConvertTo(String.class, dom);
405                RestsDefinition copy = ModelHelper.createModelFromXml(context, xml, RestsDefinition.class);
406                xml = ModelHelper.dumpModelAsXml(context, copy);
407            }
408        }
409
410        return xml;
411    }
412
413    public String dumpRoutesAsXml() throws Exception {
414        return dumpRoutesAsXml(false);
415    }
416
417    @Override
418    public String dumpRoutesAsXml(boolean resolvePlaceholders) throws Exception {
419        List<RouteDefinition> routes = context.getRouteDefinitions();
420        if (routes.isEmpty()) {
421            return null;
422        }
423
424        // use a routes definition to dump the routes
425        RoutesDefinition def = new RoutesDefinition();
426        def.setRoutes(routes);
427        String xml = ModelHelper.dumpModelAsXml(context, def);
428
429        // if resolving placeholders we parse the xml, and resolve the property placeholders during parsing
430        if (resolvePlaceholders) {
431            final AtomicBoolean changed = new AtomicBoolean();
432            InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));
433            Document dom = XmlLineNumberParser.parseXml(is, new XmlLineNumberParser.XmlTextTransformer() {
434                @Override
435                public String transform(String text) {
436                    try {
437                        String after = getContext().resolvePropertyPlaceholders(text);
438                        if (!changed.get()) {
439                            changed.set(!text.equals(after));
440                        }
441                        return after;
442                    } catch (Exception e) {
443                        // ignore
444                        return text;
445                    }
446                }
447            });
448            // okay there were some property placeholder replaced so re-create the model
449            if (changed.get()) {
450                xml = context.getTypeConverter().mandatoryConvertTo(String.class, dom);
451                RoutesDefinition copy = ModelHelper.createModelFromXml(context, xml, RoutesDefinition.class);
452                xml = ModelHelper.dumpModelAsXml(context, copy);
453            }
454        }
455
456        return xml;
457    }
458
459    public void addOrUpdateRoutesFromXml(String xml) throws Exception {
460        // do not decode so we function as before
461        addOrUpdateRoutesFromXml(xml, false);
462    }
463
464    public void addOrUpdateRoutesFromXml(String xml, boolean urlDecode) throws Exception {
465        // decode String as it may have been encoded, from its xml source
466        if (urlDecode) {
467            xml = URLDecoder.decode(xml, "UTF-8");
468        }
469
470        InputStream is = context.getTypeConverter().mandatoryConvertTo(InputStream.class, xml);
471        try {
472            // add will remove existing route first
473            context.addRouteDefinitions(is);
474        } catch (Exception e) {
475            // log the error as warn as the management api may be invoked remotely over JMX which does not propagate such exception
476            String msg = "Error updating routes from xml: " + xml + " due: " + e.getMessage();
477            LOG.warn(msg, e);
478            throw e;
479        }
480    }
481
482    public String dumpRoutesStatsAsXml(boolean fullStats, boolean includeProcessors) throws Exception {
483        StringBuilder sb = new StringBuilder();
484        sb.append("<camelContextStat").append(String.format(" id=\"%s\" state=\"%s\"", getCamelId(), getState()));
485        // use substring as we only want the attributes
486        String stat = dumpStatsAsXml(fullStats);
487        sb.append(" exchangesInflight=\"").append(getInflightExchanges()).append("\"");
488        sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
489
490        MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
491        if (server != null) {
492            // gather all the routes for this CamelContext, which requires JMX
493            String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
494            ObjectName query = ObjectName.getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=routes,*");
495            Set<ObjectName> routes = server.queryNames(query, null);
496
497            List<ManagedProcessorMBean> processors = new ArrayList<>();
498            if (includeProcessors) {
499                // gather all the processors for this CamelContext, which requires JMX
500                query = ObjectName.getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=processors,*");
501                Set<ObjectName> names = server.queryNames(query, null);
502                for (ObjectName on : names) {
503                    ManagedProcessorMBean processor = context.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedProcessorMBean.class);
504                    processors.add(processor);
505                }
506            }
507            processors.sort(new OrderProcessorMBeans());
508
509            // loop the routes, and append the processor stats if needed
510            sb.append("  <routeStats>\n");
511            for (ObjectName on : routes) {
512                ManagedRouteMBean route = context.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedRouteMBean.class);
513                sb.append("    <routeStat").append(String.format(" id=\"%s\" state=\"%s\"", route.getRouteId(), route.getState()));
514                // use substring as we only want the attributes
515                stat = route.dumpStatsAsXml(fullStats);
516                sb.append(" exchangesInflight=\"").append(route.getExchangesInflight()).append("\"");
517                sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
518
519                // add processor details if needed
520                if (includeProcessors) {
521                    sb.append("      <processorStats>\n");
522                    for (ManagedProcessorMBean processor : processors) {
523                        // the processor must belong to this route
524                        if (route.getRouteId().equals(processor.getRouteId())) {
525                            sb.append("        <processorStat").append(String.format(" id=\"%s\" index=\"%s\" state=\"%s\"", processor.getProcessorId(), processor.getIndex(), processor.getState()));
526                            // use substring as we only want the attributes
527                            stat = processor.dumpStatsAsXml(fullStats);
528                            sb.append(" exchangesInflight=\"").append(processor.getExchangesInflight()).append("\"");
529                            sb.append(" ").append(stat.substring(7)).append("\n");
530                        }
531                    }
532                    sb.append("      </processorStats>\n");
533                }
534                sb.append("    </routeStat>\n");
535            }
536            sb.append("  </routeStats>\n");
537        }
538
539        sb.append("</camelContextStat>");
540        return sb.toString();
541    }
542
543    public String dumpStepStatsAsXml(boolean fullStats) throws Exception {
544        StringBuilder sb = new StringBuilder();
545        sb.append("<camelContextStat").append(String.format(" id=\"%s\" state=\"%s\"", getCamelId(), getState()));
546        // use substring as we only want the attributes
547        String stat = dumpStatsAsXml(fullStats);
548        sb.append(" exchangesInflight=\"").append(getInflightExchanges()).append("\"");
549        sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
550
551        MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
552        if (server != null) {
553            // gather all the routes for this CamelContext, which requires JMX
554            String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
555            ObjectName query = ObjectName.getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=routes,*");
556            Set<ObjectName> routes = server.queryNames(query, null);
557
558            List<ManagedProcessorMBean> steps = new ArrayList<>();
559            // gather all the steps for this CamelContext, which requires JMX
560            query = ObjectName.getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=steps,*");
561            Set<ObjectName> names = server.queryNames(query, null);
562            for (ObjectName on : names) {
563                ManagedStepMBean step = context.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedStepMBean.class);
564                steps.add(step);
565            }
566            steps.sort(new OrderProcessorMBeans());
567
568            // loop the routes, and append the processor stats if needed
569            sb.append("  <routeStats>\n");
570            for (ObjectName on : routes) {
571                ManagedRouteMBean route = context.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedRouteMBean.class);
572                sb.append("    <routeStat").append(String.format(" id=\"%s\" state=\"%s\"", route.getRouteId(), route.getState()));
573                // use substring as we only want the attributes
574                stat = route.dumpStatsAsXml(fullStats);
575                sb.append(" exchangesInflight=\"").append(route.getExchangesInflight()).append("\"");
576                sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
577
578                // add steps details if needed
579                sb.append("      <stepStats>\n");
580                for (ManagedProcessorMBean processor : steps) {
581                    // the step must belong to this route
582                    if (route.getRouteId().equals(processor.getRouteId())) {
583                        sb.append("        <stepStat").append(String.format(" id=\"%s\" index=\"%s\" state=\"%s\"", processor.getProcessorId(), processor.getIndex(), processor.getState()));
584                        // use substring as we only want the attributes
585                        stat = processor.dumpStatsAsXml(fullStats);
586                        sb.append(" exchangesInflight=\"").append(processor.getExchangesInflight()).append("\"");
587                        sb.append(" ").append(stat.substring(7)).append("\n");
588                    }
589                    sb.append("      </stepStats>\n");
590                }
591                sb.append("    </stepStat>\n");
592            }
593            sb.append("  </routeStats>\n");
594        }
595
596        sb.append("</camelContextStat>");
597        return sb.toString();
598    }
599
600    public String dumpRoutesCoverageAsXml() throws Exception {
601        StringBuilder sb = new StringBuilder();
602        sb.append("<camelContextRouteCoverage")
603                .append(String.format(" id=\"%s\" exchangesTotal=\"%s\" totalProcessingTime=\"%s\"", getCamelId(), getExchangesTotal(), getTotalProcessingTime()))
604                .append(">\n");
605
606        String xml = dumpRoutesAsXml();
607        if (xml != null) {
608            // use the coverage xml parser to dump the routes and enrich with coverage stats
609            Document dom = RouteCoverageXmlParser.parseXml(context, new ByteArrayInputStream(xml.getBytes()));
610            // convert dom back to xml
611            String converted = context.getTypeConverter().convertTo(String.class, dom);
612            sb.append(converted);
613        }
614
615        sb.append("\n</camelContextRouteCoverage>");
616        return sb.toString();
617    }
618
619    public boolean createEndpoint(String uri) throws Exception {
620        if (context.hasEndpoint(uri) != null) {
621            // endpoint already exists
622            return false;
623        }
624
625        Endpoint endpoint = context.getEndpoint(uri);
626        if (endpoint != null) {
627            // ensure endpoint is registered, as the management strategy could have been configured to not always
628            // register new endpoints in JMX, so we need to check if its registered, and if not register it manually
629            ObjectName on = context.getManagementStrategy().getManagementObjectNameStrategy().getObjectNameForEndpoint(endpoint);
630            if (on != null && !context.getManagementStrategy().getManagementAgent().isRegistered(on)) {
631                // register endpoint as mbean
632                Object me = context.getManagementStrategy().getManagementObjectStrategy().getManagedObjectForEndpoint(context, endpoint);
633                context.getManagementStrategy().getManagementAgent().register(me, on);
634            }
635            return true;
636        } else {
637            return false;
638        }
639    }
640
641    public int removeEndpoints(String pattern) throws Exception {
642        // endpoints is always removed from JMX if removed from context
643        Collection<Endpoint> removed = context.removeEndpoints(pattern);
644        return removed.size();
645    }
646
647    public Map<String, Properties> findEips() throws Exception {
648        return context.adapt(CatalogCamelContext.class).findEips();
649    }
650
651    public List<String> findEipNames() throws Exception {
652        Map<String, Properties> map = findEips();
653        return new ArrayList<>(map.keySet());
654    }
655
656    public TabularData listEips() throws Exception {
657        try {
658            // find all EIPs
659            Map<String, Properties> eips = context.adapt(CatalogCamelContext.class).findEips();
660
661            TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.listEipsTabularType());
662
663            // gather EIP detail for each eip
664            for (Map.Entry<String, Properties> entry : eips.entrySet()) {
665                String name = entry.getKey();
666                String title = (String) entry.getValue().get("title");
667                String description = (String) entry.getValue().get("description");
668                String label = (String) entry.getValue().get("label");
669                String type = (String) entry.getValue().get("class");
670                String status = ModelCamelContextHelper.isEipInUse(context, name) ? "in use" : "on classpath";
671                CompositeType ct = CamelOpenMBeanTypes.listEipsCompositeType();
672                CompositeData data = new CompositeDataSupport(ct, new String[]{"name", "title", "description", "label", "status", "type"},
673                        new Object[]{name, title, description, label, status, type});
674                answer.put(data);
675            }
676            return answer;
677        } catch (Exception e) {
678            throw RuntimeCamelException.wrapRuntimeCamelException(e);
679        }
680    }
681
682    public Map<String, Properties> findComponents() throws Exception {
683        Map<String, Properties> answer = context.adapt(CatalogCamelContext.class).findComponents();
684        for (Map.Entry<String, Properties> entry : answer.entrySet()) {
685            if (entry.getValue() != null) {
686                // remove component as its not serializable over JMX
687                entry.getValue().remove("component");
688                // .. and components which just list all the components in the JAR/bundle and that is verbose and not needed
689                entry.getValue().remove("components");
690            }
691        }
692        return answer;
693    }
694
695    public String createRouteStaticEndpointJson() {
696        return createRouteStaticEndpointJson(true);
697    }
698
699    public String createRouteStaticEndpointJson(boolean includeDynamic) {
700        return context.adapt(CatalogCamelContext.class).createRouteStaticEndpointJson(null, includeDynamic);
701    }
702
703    public List<String> findComponentNames() throws Exception {
704        Map<String, Properties> map = findComponents();
705        return new ArrayList<>(map.keySet());
706    }
707
708    @Override
709    public TabularData listComponents() throws Exception {
710        try {
711            // find all components
712            Map<String, Properties> components = context.adapt(CatalogCamelContext.class).findComponents();
713
714            TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.listComponentsTabularType());
715
716            // gather component detail for each component
717            for (Map.Entry<String, Properties> entry : components.entrySet()) {
718                String name = entry.getKey();
719                String title = null;
720                String syntax = null;
721                String description = null;
722                String label = null;
723                String deprecated = null;
724                String secret = null;
725                String status = context.hasComponent(name) != null ? "in use" : "on classpath";
726                String type = (String) entry.getValue().get("class");
727                String groupId = null;
728                String artifactId = null;
729                String version = null;
730
731                // a component may have been given a different name, so resolve its default name by its java type
732                // as we can find the component json information from the default component name
733                String defaultName = context.adapt(CatalogCamelContext.class).resolveComponentDefaultName(type);
734                String target = defaultName != null ? defaultName : name;
735
736                // load component json data, and parse it to gather the component meta-data
737                String json = context.adapt(CatalogCamelContext.class).getComponentParameterJsonSchema(target);
738                List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("component", json, false);
739                for (Map<String, String> row : rows) {
740                    if (row.containsKey("title")) {
741                        title = row.get("title");
742                    } else if (row.containsKey("syntax")) {
743                        syntax = row.get("syntax");
744                    } else if (row.containsKey("description")) {
745                        description = row.get("description");
746                    } else if (row.containsKey("label")) {
747                        label = row.get("label");
748                    } else if (row.containsKey("deprecated")) {
749                        deprecated = row.get("deprecated");
750                    } else if (row.containsKey("secret")) {
751                        secret = row.get("secret");
752                    } else if (row.containsKey("javaType")) {
753                        type = row.get("javaType");
754                    } else if (row.containsKey("groupId")) {
755                        groupId = row.get("groupId");
756                    } else if (row.containsKey("artifactId")) {
757                        artifactId = row.get("artifactId");
758                    } else if (row.containsKey("version")) {
759                        version = row.get("version");
760                    }
761                }
762
763                CompositeType ct = CamelOpenMBeanTypes.listComponentsCompositeType();
764                CompositeData data = new CompositeDataSupport(ct,
765                        new String[]{"name", "title", "syntax", "description", "label", "deprecated", "secret", "status", "type", "groupId", "artifactId", "version"},
766                        new Object[]{name, title, syntax, description, label, deprecated, secret, status, type, groupId, artifactId, version});
767                answer.put(data);
768            }
769            return answer;
770        } catch (Exception e) {
771            throw RuntimeCamelException.wrapRuntimeCamelException(e);
772        }
773    }
774
775    public String componentParameterJsonSchema(String componentName) throws Exception {
776        return context.adapt(CatalogCamelContext.class).getComponentParameterJsonSchema(componentName);
777    }
778
779    public String dataFormatParameterJsonSchema(String dataFormatName) throws Exception {
780        return context.adapt(CatalogCamelContext.class).getDataFormatParameterJsonSchema(dataFormatName);
781    }
782
783    public String languageParameterJsonSchema(String languageName) throws Exception {
784        return context.adapt(CatalogCamelContext.class).getLanguageParameterJsonSchema(languageName);
785    }
786
787    public String eipParameterJsonSchema(String eipName) throws Exception {
788        return context.adapt(CatalogCamelContext.class).getEipParameterJsonSchema(eipName);
789    }
790
791    public String explainEipJson(String nameOrId, boolean includeAllOptions) {
792        return context.adapt(CatalogCamelContext.class).explainEipJson(nameOrId, includeAllOptions);
793    }
794
795    public String explainComponentJson(String componentName, boolean includeAllOptions) throws Exception {
796        return context.adapt(CatalogCamelContext.class).explainComponentJson(componentName, includeAllOptions);
797    }
798
799    public String explainEndpointJson(String uri, boolean includeAllOptions) throws Exception {
800        return context.adapt(CatalogCamelContext.class).explainEndpointJson(uri, includeAllOptions);
801    }
802
803    public void reset(boolean includeRoutes) throws Exception {
804        reset();
805
806        // and now reset all routes for this route
807        if (includeRoutes) {
808            MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
809            if (server != null) {
810                String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
811                ObjectName query = ObjectName.getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=routes,*");
812                Set<ObjectName> names = server.queryNames(query, null);
813                for (ObjectName name : names) {
814                    server.invoke(name, "reset", new Object[]{true}, new String[]{"boolean"});
815                }
816            }
817        }
818    }
819
820    /**
821     * Used for sorting the processor mbeans accordingly to their index.
822     */
823    private static final class OrderProcessorMBeans implements Comparator<ManagedProcessorMBean> {
824
825        @Override
826        public int compare(ManagedProcessorMBean o1, ManagedProcessorMBean o2) {
827            return o1.getIndex().compareTo(o2.getIndex());
828        }
829    }
830
831}