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.IOException;
020import java.io.InputStream;
021import java.net.URLDecoder;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.Comparator;
026import java.util.List;
027import java.util.Map;
028import java.util.Properties;
029import java.util.Set;
030import java.util.concurrent.TimeUnit;
031import javax.management.MBeanServer;
032import javax.management.MBeanServerInvocationHandler;
033import javax.management.ObjectName;
034
035import org.apache.camel.CamelContext;
036import org.apache.camel.Component;
037import org.apache.camel.ComponentConfiguration;
038import org.apache.camel.Endpoint;
039import org.apache.camel.ManagementStatisticsLevel;
040import org.apache.camel.Producer;
041import org.apache.camel.ProducerTemplate;
042import org.apache.camel.Route;
043import org.apache.camel.TimerListener;
044import org.apache.camel.api.management.ManagedResource;
045import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
046import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
047import org.apache.camel.api.management.mbean.ManagedRouteMBean;
048import org.apache.camel.model.ModelCamelContext;
049import org.apache.camel.model.ModelHelper;
050import org.apache.camel.model.RouteDefinition;
051import org.apache.camel.model.RoutesDefinition;
052import org.apache.camel.model.rest.RestDefinition;
053import org.apache.camel.model.rest.RestsDefinition;
054
055/**
056 * @version 
057 */
058@ManagedResource(description = "Managed CamelContext")
059public class ManagedCamelContext extends ManagedPerformanceCounter implements TimerListener, ManagedCamelContextMBean {
060    private final ModelCamelContext context;   
061    private final LoadTriplet load = new LoadTriplet();
062
063    public ManagedCamelContext(ModelCamelContext context) {
064        this.context = context;
065        boolean enabled = context.getManagementStrategy().getStatisticsLevel() != ManagementStatisticsLevel.Off;
066        setStatisticsEnabled(enabled);
067    }
068
069    public CamelContext getContext() {
070        return context;
071    }
072
073    public String getCamelId() {
074        return context.getName();
075    }
076
077    public String getManagementName() {
078        return context.getManagementName();
079    }
080
081    public String getCamelVersion() {
082        return context.getVersion();
083    }
084
085    public String getState() {
086        return context.getStatus().name();
087    }
088
089    public String getUptime() {
090        return context.getUptime();
091    }
092
093    public String getClassResolver() {
094        return context.getClassResolver().getClass().getName();
095    }
096
097    public String getPackageScanClassResolver() {
098        return context.getPackageScanClassResolver().getClass().getName();
099    }
100
101    public String getApplicationContextClassName() {
102        if (context.getApplicationContextClassLoader() != null) {
103            return context.getApplicationContextClassLoader().toString();
104        } else {
105            return null;
106        }
107    }
108
109    public Map<String, String> getProperties() {
110        if (context.getProperties().isEmpty()) {
111            return null;
112        }
113        return context.getProperties();
114    }
115    
116    public String getProperty(String name) throws Exception {
117        return context.getProperty(name);
118    }
119
120    public void setProperty(String name, String value) throws Exception {
121        context.getProperties().put(name, value);
122    }
123
124    public Boolean getTracing() {
125        return context.isTracing();
126    }
127
128    public void setTracing(Boolean tracing) {
129        context.setTracing(tracing);
130    }
131
132    public Integer getInflightExchanges() {
133        return context.getInflightRepository().size();
134    }
135
136    public Integer getTotalRoutes() {
137        return context.getRoutes().size();
138    }
139
140    public Integer getStartedRoutes() {
141        int started = 0;
142        for (Route route : context.getRoutes()) {
143            if (context.getRouteStatus(route.getId()).isStarted()) {
144                started++;
145            }
146        }
147        return started;
148    }
149
150    public void setTimeout(long timeout) {
151        context.getShutdownStrategy().setTimeout(timeout);
152    }
153
154    public long getTimeout() {
155        return context.getShutdownStrategy().getTimeout();
156    }
157
158    public void setTimeUnit(TimeUnit timeUnit) {
159        context.getShutdownStrategy().setTimeUnit(timeUnit);
160    }
161
162    public TimeUnit getTimeUnit() {
163        return context.getShutdownStrategy().getTimeUnit();
164    }
165
166    public void setShutdownNowOnTimeout(boolean shutdownNowOnTimeout) {
167        context.getShutdownStrategy().setShutdownNowOnTimeout(shutdownNowOnTimeout);
168    }
169
170    public boolean isShutdownNowOnTimeout() {
171        return context.getShutdownStrategy().isShutdownNowOnTimeout();
172    }
173
174    public String getLoad01() {
175        double load1 = load.getLoad1();
176        if (Double.isNaN(load1)) {
177            // empty string if load statistics is disabled
178            return "";
179        } else {
180            return String.format("%.2f", load1);
181        }
182    }
183
184    public String getLoad05() {
185        double load5 = load.getLoad5();
186        if (Double.isNaN(load5)) {
187            // empty string if load statistics is disabled
188            return "";
189        } else {
190            return String.format("%.2f", load5);
191        }
192    }
193
194    public String getLoad15() {
195        double load15 = load.getLoad15();
196        if (Double.isNaN(load15)) {
197            // empty string if load statistics is disabled
198            return "";
199        } else {
200            return String.format("%.2f", load15);
201        }
202    }
203
204    public boolean isUseBreadcrumb() {
205        return context.isUseBreadcrumb();
206    }
207
208    public boolean isAllowUseOriginalMessage() {
209        return context.isAllowUseOriginalMessage();
210    }
211
212    public boolean isMessageHistory() {
213        return context.isMessageHistory() != null ? context.isMessageHistory() : false;
214    }
215
216    public boolean isUseMDCLogging() {
217        return context.isUseMDCLogging();
218    }
219
220    public void onTimer() {
221        load.update(getInflightExchanges());
222    }
223
224    public void start() throws Exception {
225        if (context.isSuspended()) {
226            context.resume();
227        } else {
228            context.start();
229        }
230    }
231
232    public void stop() throws Exception {
233        context.stop();
234    }
235
236    public void restart() throws Exception {
237        context.stop();
238        context.start();
239    }
240
241    public void suspend() throws Exception {
242        context.suspend();
243    }
244
245    public void resume() throws Exception {
246        if (context.isSuspended()) {
247            context.resume();
248        } else {
249            throw new IllegalStateException("CamelContext is not suspended");
250        }
251    }
252
253    public void startAllRoutes() throws Exception {
254        context.startAllRoutes();
255    }
256
257    public boolean canSendToEndpoint(String endpointUri) {
258        try {
259            Endpoint endpoint = context.getEndpoint(endpointUri);
260            if (endpoint != null) {
261                Producer producer = endpoint.createProducer();
262                return producer != null;
263            }
264        } catch (Exception e) {
265            // ignore
266        }
267
268        return false;
269    }
270
271    public void sendBody(String endpointUri, Object body) throws Exception {
272        ProducerTemplate template = context.createProducerTemplate();
273        try {
274            template.sendBody(endpointUri, body);
275        } finally {
276            template.stop();
277        }
278    }
279
280    public void sendStringBody(String endpointUri, String body) throws Exception {
281        sendBody(endpointUri, body);
282    }
283
284    public void sendBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception {
285        ProducerTemplate template = context.createProducerTemplate();
286        try {
287            template.sendBodyAndHeaders(endpointUri, body, headers);
288        } finally {
289            template.stop();
290        }
291    }
292
293    public Object requestBody(String endpointUri, Object body) throws Exception {
294        ProducerTemplate template = context.createProducerTemplate();
295        Object answer = null;
296        try {
297            answer = template.requestBody(endpointUri, body);
298        } finally {
299            template.stop();
300        }
301        return answer;
302    }
303
304    public Object requestStringBody(String endpointUri, String body) throws Exception {
305        return requestBody(endpointUri, body);
306    }
307
308    public Object requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception {
309        ProducerTemplate template = context.createProducerTemplate();
310        Object answer = null;
311        try {
312            answer = template.requestBodyAndHeaders(endpointUri, body, headers);
313        } finally {
314            template.stop();
315        }
316        return answer;
317    }
318
319    public String dumpRestsAsXml() throws Exception {
320        List<RestDefinition> rests = context.getRestDefinitions();
321        if (rests.isEmpty()) {
322            return null;
323        }
324
325        // use a routes definition to dump the rests
326        RestsDefinition def = new RestsDefinition();
327        def.setRests(rests);
328        return ModelHelper.dumpModelAsXml(def);
329    }
330
331    public String dumpRoutesAsXml() throws Exception {
332        List<RouteDefinition> routes = context.getRouteDefinitions();
333        if (routes.isEmpty()) {
334            return null;
335        }
336
337        // use a routes definition to dump the routes
338        RoutesDefinition def = new RoutesDefinition();
339        def.setRoutes(routes);
340        return ModelHelper.dumpModelAsXml(def);
341    }
342
343    public void addOrUpdateRoutesFromXml(String xml) throws Exception {
344        // do not decode so we function as before
345        addOrUpdateRoutesFromXml(xml, false);
346    }
347
348    public void addOrUpdateRoutesFromXml(String xml, boolean urlDecode) throws Exception {
349        // decode String as it may have been encoded, from its xml source
350        if (urlDecode) {
351            xml = URLDecoder.decode(xml, "UTF-8");
352        }
353
354        InputStream is = context.getTypeConverter().mandatoryConvertTo(InputStream.class, xml);
355        RoutesDefinition def = context.loadRoutesDefinition(is);
356        if (def == null) {
357            return;
358        }
359
360        // add will remove existing route first
361        context.addRouteDefinitions(def.getRoutes());
362    }
363
364    public String dumpRoutesStatsAsXml(boolean fullStats, boolean includeProcessors) throws Exception {
365        StringBuilder sb = new StringBuilder();
366        sb.append("<camelContextStat").append(String.format(" id=\"%s\"", getCamelId()));
367        // use substring as we only want the attributes
368        String stat = dumpStatsAsXml(fullStats);
369        sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
370
371        MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
372        if (server != null) {
373            // gather all the routes for this CamelContext, which requires JMX
374            String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
375            ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*");
376            Set<ObjectName> routes = server.queryNames(query, null);
377
378            List<ManagedProcessorMBean> processors = new ArrayList<ManagedProcessorMBean>();
379            if (includeProcessors) {
380                // gather all the processors for this CamelContext, which requires JMX
381                query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=processors,*");
382                Set<ObjectName> names = server.queryNames(query, null);
383                for (ObjectName on : names) {
384                    ManagedProcessorMBean processor = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedProcessorMBean.class, true);
385                    processors.add(processor);
386                }
387            }
388            Collections.sort(processors, new OrderProcessorMBeans());
389
390            // loop the routes, and append the processor stats if needed
391            sb.append("  <routeStats>\n");
392            for (ObjectName on : routes) {
393                ManagedRouteMBean route = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedRouteMBean.class, true);
394                sb.append("    <routeStat").append(String.format(" id=\"%s\"", route.getRouteId()));
395                // use substring as we only want the attributes
396                stat = route.dumpStatsAsXml(fullStats);
397                sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
398
399                // add processor details if needed
400                if (includeProcessors) {
401                    sb.append("      <processorStats>\n");
402                    for (ManagedProcessorMBean processor : processors) {
403                        // the processor must belong to this route
404                        if (route.getRouteId().equals(processor.getRouteId())) {
405                            sb.append("        <processorStat").append(String.format(" id=\"%s\"", processor.getProcessorId()));
406                            // use substring as we only want the attributes
407                            sb.append(" ").append(processor.dumpStatsAsXml(fullStats).substring(7)).append("\n");
408                        }
409                    }
410                    sb.append("      </processorStats>\n");
411                }
412                sb.append("    </routeStat>\n");
413            }
414            sb.append("  </routeStats>\n");
415        }
416
417        sb.append("</camelContextStat>");
418        return sb.toString();
419    }
420
421    public boolean createEndpoint(String uri) throws Exception {
422        if (context.hasEndpoint(uri) != null) {
423            // endpoint already exists
424            return false;
425        }
426
427        Endpoint endpoint = context.getEndpoint(uri);
428        if (endpoint != null) {
429            // ensure endpoint is registered, as the management strategy could have been configured to not always
430            // register new endpoints in JMX, so we need to check if its registered, and if not register it manually
431            ObjectName on = context.getManagementStrategy().getManagementNamingStrategy().getObjectNameForEndpoint(endpoint);
432            if (on != null && !context.getManagementStrategy().getManagementAgent().isRegistered(on)) {
433                // register endpoint as mbean
434                Object me = context.getManagementStrategy().getManagementObjectStrategy().getManagedObjectForEndpoint(context, endpoint);
435                context.getManagementStrategy().getManagementAgent().register(me, on);
436            }
437            return true;
438        } else {
439            return false;
440        }
441    }
442
443    public int removeEndpoints(String pattern) throws Exception {
444        // endpoints is always removed from JMX if removed from context
445        Collection<Endpoint> removed = context.removeEndpoints(pattern);
446        return removed.size();
447    }
448
449    public Map<String, Properties> findComponents() throws Exception {
450        Map<String, Properties> answer = context.findComponents();
451        for (Map.Entry<String, Properties> entry : answer.entrySet()) {
452            if (entry.getValue() != null) {
453                // remove component as its not serializable over JMX
454                entry.getValue().remove("component");
455                // .. and components which just list all the components in the JAR/bundle and that is verbose and not needed
456                entry.getValue().remove("components");
457            }
458        }
459        return answer;
460    }
461
462    public String getComponentDocumentation(String componentName) throws IOException {
463        return context.getComponentDocumentation(componentName);
464    }
465
466    public String createRouteStaticEndpointJson() {
467        return createRouteStaticEndpointJson(true);
468    }
469
470    public String createRouteStaticEndpointJson(boolean includeDynamic) {
471        return context.createRouteStaticEndpointJson(null, includeDynamic);
472    }
473
474    public List<String> findComponentNames() throws Exception {
475        Map<String, Properties> map = findComponents();
476        return new ArrayList<String>(map.keySet());
477    }
478
479    public List<String> completeEndpointPath(String componentName, Map<String, Object> endpointParameters,
480                                             String completionText) throws Exception {
481        if (completionText == null) {
482            completionText = "";
483        }
484        Component component = context.getComponent(componentName, false);
485        if (component != null) {
486            ComponentConfiguration configuration = component.createComponentConfiguration();
487            configuration.setParameters(endpointParameters);
488            return configuration.completeEndpointPath(completionText);
489        } else {
490            return new ArrayList<String>();
491        }
492    }
493
494    public String componentParameterJsonSchema(String componentName) throws Exception {
495        Component component = context.getComponent(componentName);
496        if (component != null) {
497            ComponentConfiguration configuration = component.createComponentConfiguration();
498            return configuration.createParameterJsonSchema();
499        } else {
500            return null;
501        }
502    }
503
504    public void reset(boolean includeRoutes) throws Exception {
505        reset();
506
507        // and now reset all routes for this route
508        if (includeRoutes) {
509            MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
510            if (server != null) {
511                String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
512                ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*");
513                Set<ObjectName> names = server.queryNames(query, null);
514                for (ObjectName name : names) {
515                    server.invoke(name, "reset", new Object[]{true}, new String[]{"boolean"});
516                }
517            }
518        }
519    }
520
521    /**
522     * Used for sorting the processor mbeans accordingly to their index.
523     */
524    private static final class OrderProcessorMBeans implements Comparator<ManagedProcessorMBean> {
525
526        @Override
527        public int compare(ManagedProcessorMBean o1, ManagedProcessorMBean o2) {
528            return o1.getIndex().compareTo(o2.getIndex());
529        }
530    }
531
532}