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.camel.management.mbean;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    import java.net.URLDecoder;
022    import java.util.ArrayList;
023    import java.util.Collection;
024    import java.util.LinkedHashSet;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Properties;
028    import java.util.Set;
029    import java.util.concurrent.TimeUnit;
030    import javax.management.MBeanServer;
031    import javax.management.MBeanServerInvocationHandler;
032    import javax.management.ObjectName;
033    
034    import org.apache.camel.CamelContext;
035    import org.apache.camel.Component;
036    import org.apache.camel.ComponentConfiguration;
037    import org.apache.camel.Endpoint;
038    import org.apache.camel.ManagementStatisticsLevel;
039    import org.apache.camel.ProducerTemplate;
040    import org.apache.camel.Route;
041    import org.apache.camel.TimerListener;
042    import org.apache.camel.api.management.ManagedResource;
043    import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
044    import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
045    import org.apache.camel.api.management.mbean.ManagedRouteMBean;
046    import org.apache.camel.model.ModelCamelContext;
047    import org.apache.camel.model.ModelHelper;
048    import org.apache.camel.model.RouteDefinition;
049    import org.apache.camel.model.RoutesDefinition;
050    
051    /**
052     * @version 
053     */
054    @ManagedResource(description = "Managed CamelContext")
055    public class ManagedCamelContext extends ManagedPerformanceCounter implements TimerListener, ManagedCamelContextMBean {
056        private final ModelCamelContext context;   
057        private final LoadTriplet load = new LoadTriplet();
058    
059        public ManagedCamelContext(ModelCamelContext context) {
060            this.context = context;
061            boolean enabled = context.getManagementStrategy().getStatisticsLevel() != ManagementStatisticsLevel.Off;
062            setStatisticsEnabled(enabled);
063        }
064    
065        public CamelContext getContext() {
066            return context;
067        }
068    
069        public String getCamelId() {
070            return context.getName();
071        }
072    
073        public String getManagementName() {
074            return context.getManagementName();
075        }
076    
077        public String getCamelVersion() {
078            return context.getVersion();
079        }
080    
081        public String getState() {
082            return context.getStatus().name();
083        }
084    
085        public String getUptime() {
086            return context.getUptime();
087        }
088    
089        public String getClassResolver() {
090            return context.getClassResolver().getClass().getName();
091        }
092    
093        public String getPackageScanClassResolver() {
094            return context.getPackageScanClassResolver().getClass().getName();
095        }
096    
097        public String getApplicationContextClassName() {
098            if (context.getApplicationContextClassLoader() != null) {
099                return context.getApplicationContextClassLoader().toString();
100            } else {
101                return null;
102            }
103        }
104    
105        public Map<String, String> getProperties() {
106            if (context.getProperties().isEmpty()) {
107                return null;
108            }
109            return context.getProperties();
110        }
111        
112        public String getProperty(String name) throws Exception {
113            return context.getProperty(name);
114        }
115    
116        public void setProperty(String name, String value) throws Exception {
117            context.getProperties().put(name, value);
118        }
119    
120        public Boolean getTracing() {
121            return context.isTracing();
122        }
123    
124        public void setTracing(Boolean tracing) {
125            context.setTracing(tracing);
126        }
127    
128        public Boolean getMessageHistory() {
129            return context.isMessageHistory();
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();
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 sendBody(String endpointUri, Object body) throws Exception {
254            ProducerTemplate template = context.createProducerTemplate();
255            try {
256                template.sendBody(endpointUri, body);
257            } finally {
258                template.stop();
259            }
260        }
261    
262        public void sendStringBody(String endpointUri, String body) throws Exception {
263            sendBody(endpointUri, body);
264        }
265    
266        public void sendBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception {
267            ProducerTemplate template = context.createProducerTemplate();
268            try {
269                template.sendBodyAndHeaders(endpointUri, body, headers);
270            } finally {
271                template.stop();
272            }
273        }
274    
275        public Object requestBody(String endpointUri, Object body) throws Exception {
276            ProducerTemplate template = context.createProducerTemplate();
277            Object answer = null;
278            try {
279                answer = template.requestBody(endpointUri, body);
280            } finally {
281                template.stop();
282            }
283            return answer;
284        }
285    
286        public Object requestStringBody(String endpointUri, String body) throws Exception {
287            return requestBody(endpointUri, body);
288        }
289    
290        public Object requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception {
291            ProducerTemplate template = context.createProducerTemplate();
292            Object answer = null;
293            try {
294                answer = template.requestBodyAndHeaders(endpointUri, body, headers);
295            } finally {
296                template.stop();
297            }
298            return answer;
299        }
300    
301        public String dumpRoutesAsXml() throws Exception {
302            List<RouteDefinition> routes = context.getRouteDefinitions();
303            if (routes.isEmpty()) {
304                return null;
305            }
306    
307            // use a routes definition to dump the routes
308            RoutesDefinition def = new RoutesDefinition();
309            def.setRoutes(routes);
310            return ModelHelper.dumpModelAsXml(def);
311        }
312    
313        public void addOrUpdateRoutesFromXml(String xml) throws Exception {
314            // do not decode so we function as before
315            addOrUpdateRoutesFromXml(xml, false);
316        }
317    
318        public void addOrUpdateRoutesFromXml(String xml, boolean urlDecode) throws Exception {
319            // decode String as it may have been encoded, from its xml source
320            if (urlDecode) {
321                xml = URLDecoder.decode(xml, "UTF-8");
322            }
323    
324            InputStream is = context.getTypeConverter().mandatoryConvertTo(InputStream.class, xml);
325            RoutesDefinition def = context.loadRoutesDefinition(is);
326            if (def == null) {
327                return;
328            }
329    
330            // add will remove existing route first
331            context.addRouteDefinitions(def.getRoutes());
332        }
333    
334        public String dumpRoutesStatsAsXml(boolean fullStats, boolean includeProcessors) throws Exception {
335            StringBuilder sb = new StringBuilder();
336            sb.append("<camelContextStat").append(String.format(" id=\"%s\"", getCamelId()));
337            // use substring as we only want the attributes
338            String stat = dumpStatsAsXml(fullStats);
339            sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
340    
341            MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
342            if (server != null) {
343                // gather all the routes for this CamelContext, which requires JMX
344                String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
345                ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*");
346                Set<ObjectName> routes = server.queryNames(query, null);
347    
348                Set<ManagedProcessorMBean> processors = new LinkedHashSet<ManagedProcessorMBean>();
349                if (includeProcessors) {
350                    // gather all the processors for this CamelContext, which requires JMX
351                    query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=processors,*");
352                    Set<ObjectName> names = server.queryNames(query, null);
353                    for (ObjectName on : names) {
354                        ManagedProcessorMBean processor = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedProcessorMBean.class, true);
355                        processors.add(processor);
356                    }
357                }
358                
359                // loop the routes, and append the processor stats if needed
360                sb.append("  <routeStats>\n");
361                for (ObjectName on : routes) {
362                    ManagedRouteMBean route = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedRouteMBean.class, true);
363                    sb.append("    <routeStat").append(String.format(" id=\"%s\"", route.getRouteId()));
364                    // use substring as we only want the attributes
365                    stat = route.dumpStatsAsXml(fullStats);
366                    sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n");
367    
368                    // add processor details if needed
369                    if (includeProcessors) {
370                        sb.append("      <processorStats>\n");
371                        for (ManagedProcessorMBean processor : processors) {
372                            // the processor must belong to this route
373                            if (route.getRouteId().equals(processor.getRouteId())) {
374                                sb.append("        <processorStat").append(String.format(" id=\"%s\"", processor.getProcessorId()));
375                                // use substring as we only want the attributes
376                                sb.append(" ").append(processor.dumpStatsAsXml(fullStats).substring(7)).append("\n");
377                            }
378                        }
379                        sb.append("      </processorStats>\n");
380                    }
381                    sb.append("    </routeStat>\n");
382                }
383                sb.append("  </routeStats>\n");
384            }
385    
386            sb.append("</camelContextStat>");
387            return sb.toString();
388        }
389    
390        public boolean createEndpoint(String uri) throws Exception {
391            if (context.hasEndpoint(uri) != null) {
392                // endpoint already exists
393                return false;
394            }
395    
396            Endpoint endpoint = context.getEndpoint(uri);
397            if (endpoint != null) {
398                // ensure endpoint is registered, as the management strategy could have been configured to not always
399                // register new endpoints in JMX, so we need to check if its registered, and if not register it manually
400                ObjectName on = context.getManagementStrategy().getManagementNamingStrategy().getObjectNameForEndpoint(endpoint);
401                if (on != null && !context.getManagementStrategy().getManagementAgent().isRegistered(on)) {
402                    // register endpoint as mbean
403                    Object me = context.getManagementStrategy().getManagementObjectStrategy().getManagedObjectForEndpoint(context, endpoint);
404                    context.getManagementStrategy().getManagementAgent().register(me, on);
405                }
406                return true;
407            } else {
408                return false;
409            }
410        }
411    
412        public int removeEndpoints(String pattern) throws Exception {
413            // endpoints is always removed from JMX if removed from context
414            Collection<Endpoint> removed = context.removeEndpoints(pattern);
415            return removed.size();
416        }
417    
418        public Map<String, Properties> findComponents() throws Exception {
419            Map<String, Properties> answer = context.findComponents();
420            for (Map.Entry<String, Properties> entry : answer.entrySet()) {
421                if (entry.getValue() != null) {
422                    // remove component as its not serializable over JMX
423                    entry.getValue().remove("component");
424                    // .. and components which just list all the components in the JAR/bundle and that is verbose and not needed
425                    entry.getValue().remove("components");
426                }
427            }
428            return answer;
429        }
430    
431        public String getComponentDocumentation(String componentName) throws IOException {
432            return context.getComponentDocumentation(componentName);
433        }
434    
435        public String createRouteStaticEndpointJson() {
436            return context.createRouteStaticEndpointJson(null);
437        }
438    
439        public List<String> findComponentNames() throws Exception {
440            Map<String, Properties> map = findComponents();
441            return new ArrayList<String>(map.keySet());
442        }
443    
444        public List<String> completeEndpointPath(String componentName, Map<String, Object> endpointParameters,
445                                                 String completionText) throws Exception {
446            if (completionText == null) {
447                completionText = "";
448            }
449            Component component = context.getComponent(componentName, false);
450            if (component != null) {
451                ComponentConfiguration configuration = component.createComponentConfiguration();
452                configuration.setParameters(endpointParameters);
453                return configuration.completeEndpointPath(completionText);
454            } else {
455                return new ArrayList<String>();
456            }
457        }
458    
459        public String componentParameterJsonSchema(String componentName) throws Exception {
460            Component component = context.getComponent(componentName);
461            if (component != null) {
462                ComponentConfiguration configuration = component.createComponentConfiguration();
463                return configuration.createParameterJsonSchema();
464            } else {
465                return null;
466            }
467        }
468    
469        public void reset(boolean includeRoutes) throws Exception {
470            reset();
471    
472            // and now reset all routes for this route
473            if (includeRoutes) {
474                MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
475                if (server != null) {
476                    String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
477                    ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*");
478                    Set<ObjectName> names = server.queryNames(query, null);
479                    for (ObjectName name : names) {
480                        server.invoke(name, "reset", new Object[]{true}, new String[]{"boolean"});
481                    }
482                }
483            }
484        }
485    
486    }