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;
018
019import java.net.UnknownHostException;
020import java.util.concurrent.ThreadPoolExecutor;
021
022import javax.management.MalformedObjectNameException;
023import javax.management.ObjectName;
024
025import org.apache.camel.CamelContext;
026import org.apache.camel.CamelContextAware;
027import org.apache.camel.Component;
028import org.apache.camel.Consumer;
029import org.apache.camel.Endpoint;
030import org.apache.camel.ErrorHandlerFactory;
031import org.apache.camel.NamedNode;
032import org.apache.camel.Processor;
033import org.apache.camel.Producer;
034import org.apache.camel.Route;
035import org.apache.camel.Service;
036import org.apache.camel.StaticService;
037import org.apache.camel.builder.ErrorHandlerBuilderRef;
038import org.apache.camel.cluster.CamelClusterService;
039import org.apache.camel.management.mbean.ManagedBacklogDebugger;
040import org.apache.camel.management.mbean.ManagedBacklogTracer;
041import org.apache.camel.management.mbean.ManagedCamelContext;
042import org.apache.camel.management.mbean.ManagedCamelHealth;
043import org.apache.camel.management.mbean.ManagedClusterService;
044import org.apache.camel.management.mbean.ManagedComponent;
045import org.apache.camel.management.mbean.ManagedConsumer;
046import org.apache.camel.management.mbean.ManagedDataFormat;
047import org.apache.camel.management.mbean.ManagedEndpoint;
048import org.apache.camel.management.mbean.ManagedErrorHandler;
049import org.apache.camel.management.mbean.ManagedEventNotifier;
050import org.apache.camel.management.mbean.ManagedProcessor;
051import org.apache.camel.management.mbean.ManagedProducer;
052import org.apache.camel.management.mbean.ManagedRoute;
053import org.apache.camel.management.mbean.ManagedRouteController;
054import org.apache.camel.management.mbean.ManagedService;
055import org.apache.camel.management.mbean.ManagedThreadPool;
056import org.apache.camel.spi.DataFormat;
057import org.apache.camel.spi.EventNotifier;
058import org.apache.camel.spi.ManagementObjectNameStrategy;
059import org.apache.camel.spi.RouteContext;
060import org.apache.camel.util.InetAddressUtil;
061import org.apache.camel.util.ObjectHelper;
062import org.apache.camel.util.URISupport;
063
064/**
065 * Naming strategy used when registering MBeans.
066 */
067public class DefaultManagementObjectNameStrategy implements ManagementObjectNameStrategy, CamelContextAware {
068    public static final String VALUE_UNKNOWN = "unknown";
069    public static final String KEY_NAME = "name";
070    public static final String KEY_TYPE = "type";
071    public static final String KEY_CONTEXT = "context";
072    public static final String TYPE_CONTEXT = "context";
073    public static final String TYPE_ROUTE_CONTROLLER = "routecontrollers";
074    public static final String TYPE_HEALTH = "health";
075    public static final String TYPE_ENDPOINT = "endpoints";
076    public static final String TYPE_DATAFORMAT = "dataformats";
077    public static final String TYPE_PROCESSOR = "processors";
078    public static final String TYPE_CONSUMER = "consumers";
079    public static final String TYPE_PRODUCER = "producers";
080    public static final String TYPE_ROUTE = "routes";
081    public static final String TYPE_COMPONENT = "components";
082    public static final String TYPE_TRACER = "tracer";
083    public static final String TYPE_EVENT_NOTIFIER = "eventnotifiers";
084    public static final String TYPE_ERRORHANDLER = "errorhandlers";
085    public static final String TYPE_THREAD_POOL = "threadpools";
086    public static final String TYPE_SERVICE = "services";
087    public static final String TYPE_HA = "clusterservices";
088
089    protected String domainName;
090    protected String hostName = "localhost";
091    protected CamelContext camelContext;
092
093    public DefaultManagementObjectNameStrategy() {
094        this(null);
095        // default constructor needed for <bean> style configuration
096    }
097
098    public DefaultManagementObjectNameStrategy(String domainName) {
099        this.domainName = domainName != null ? domainName : "org.apache.camel";
100        try {
101            hostName = InetAddressUtil.getLocalHostName();
102        } catch (UnknownHostException ex) {
103            // ignore, use the default "localhost"
104        }
105    }
106
107    public CamelContext getCamelContext() {
108        return camelContext;
109    }
110
111    public void setCamelContext(CamelContext camelContext) {
112        this.camelContext = camelContext;
113    }
114
115    public ObjectName getObjectName(Object managedObject) throws MalformedObjectNameException {
116        if (managedObject == null) {
117            return null;
118        }
119        ObjectName objectName = null;
120        if (managedObject instanceof ManagedCamelContext) {
121            ManagedCamelContext mcc = (ManagedCamelContext) managedObject;
122            objectName = getObjectNameForCamelContext(mcc.getContext());
123        } else if (managedObject instanceof ManagedCamelHealth) {
124            ManagedCamelHealth mch = (ManagedCamelHealth) managedObject;
125            objectName = getObjectNameForCamelHealth(mch.getContext());
126        } else if (managedObject instanceof ManagedRouteController) {
127            ManagedRouteController mrc = (ManagedRouteController) managedObject;
128            objectName = getObjectNameForRouteController(mrc.getContext());
129        } else if (managedObject instanceof ManagedComponent) {
130            ManagedComponent mc = (ManagedComponent) managedObject;
131            objectName = getObjectNameForComponent(mc.getComponent(), mc.getComponentName());
132        } else if (managedObject instanceof ManagedDataFormat) {
133            ManagedDataFormat md = (ManagedDataFormat) managedObject;
134            objectName = getObjectNameForDataFormat(md.getContext(), md.getDataFormat());
135        } else if (managedObject instanceof ManagedEndpoint) {
136            ManagedEndpoint me = (ManagedEndpoint) managedObject;
137            objectName = getObjectNameForEndpoint(me.getEndpoint());
138        } else if (managedObject instanceof Endpoint) {
139            objectName = getObjectNameForEndpoint((Endpoint) managedObject);
140        } else if (managedObject instanceof ManagedRoute) {
141            ManagedRoute mr = (ManagedRoute) managedObject;
142            objectName = getObjectNameForRoute(mr.getRoute());
143        } else if (managedObject instanceof ManagedErrorHandler) {
144            ManagedErrorHandler meh = (ManagedErrorHandler) managedObject;
145            objectName = getObjectNameForErrorHandler(meh.getRouteContext(), meh.getErrorHandler(), meh.getErrorHandlerBuilder());
146        } else if (managedObject instanceof ManagedProcessor) {
147            ManagedProcessor mp = (ManagedProcessor) managedObject;
148            objectName = getObjectNameForProcessor(mp.getContext(), mp.getProcessor(), mp.getDefinition());
149        } else if (managedObject instanceof ManagedConsumer) {
150            ManagedConsumer ms = (ManagedConsumer) managedObject;
151            objectName = getObjectNameForConsumer(ms.getContext(), ms.getConsumer());
152        } else if (managedObject instanceof ManagedProducer) {
153            ManagedProducer ms = (ManagedProducer) managedObject;
154            objectName = getObjectNameForProducer(ms.getContext(), ms.getProducer());
155        } else if (managedObject instanceof ManagedBacklogTracer) {
156            ManagedBacklogTracer mt = (ManagedBacklogTracer) managedObject;
157            objectName = getObjectNameForTracer(mt.getContext(), mt.getBacklogTracer());
158        } else if (managedObject instanceof ManagedBacklogDebugger) {
159            ManagedBacklogDebugger md = (ManagedBacklogDebugger) managedObject;
160            objectName = getObjectNameForTracer(md.getContext(), md.getBacklogDebugger());
161        } else if (managedObject instanceof ManagedEventNotifier) {
162            ManagedEventNotifier men = (ManagedEventNotifier) managedObject;
163            objectName = getObjectNameForEventNotifier(men.getContext(), men.getEventNotifier());
164        } else if (managedObject instanceof ManagedThreadPool) {
165            ManagedThreadPool mes = (ManagedThreadPool) managedObject;
166            objectName = getObjectNameForThreadPool(mes.getContext(), mes.getThreadPool(), mes.getId(), mes.getSourceId());
167        } else if (managedObject instanceof ManagedClusterService) {
168            ManagedClusterService mcs = (ManagedClusterService) managedObject;
169            objectName = getObjectNameForClusterService(mcs.getContext(), mcs.getService());
170        } else if (managedObject instanceof ManagedService) {
171            // check for managed service should be last
172            ManagedService ms = (ManagedService) managedObject;
173            // skip endpoints as they are already managed
174            if (ms.getService() instanceof Endpoint) {
175                return null;
176            }
177            objectName = getObjectNameForService(ms.getContext(), ms.getService());
178        }
179
180        return objectName;
181    }
182
183    public ObjectName getObjectNameForCamelContext(String managementName, String name) throws MalformedObjectNameException {
184        StringBuilder buffer = new StringBuilder();
185        buffer.append(domainName).append(":");
186        buffer.append(KEY_CONTEXT + "=").append(getContextId(managementName)).append(",");
187        buffer.append(KEY_TYPE + "=" + TYPE_CONTEXT + ",");
188        buffer.append(KEY_NAME + "=").append(ObjectName.quote(name));
189        return createObjectName(buffer);
190    }
191
192    public ObjectName getObjectNameForCamelContext(CamelContext context) throws MalformedObjectNameException {
193        // prefer to use the given management name if previously assigned
194        String managementName = context.getManagementName();
195        if (managementName == null) {
196            managementName = context.getManagementNameStrategy().getName();
197        }
198        String name = context.getName();
199        return getObjectNameForCamelContext(managementName, name);
200    }
201
202    @Override
203    public ObjectName getObjectNameForCamelHealth(CamelContext context) throws MalformedObjectNameException {
204        // prefer to use the given management name if previously assigned
205        String managementName = context.getManagementName();
206        if (managementName == null) {
207            managementName = context.getManagementNameStrategy().getName();
208        }
209
210        StringBuilder buffer = new StringBuilder();
211        buffer.append(domainName).append(":");
212        buffer.append(KEY_CONTEXT + "=").append(getContextId(managementName)).append(",");
213        buffer.append(KEY_TYPE + "=" + TYPE_HEALTH + ",");
214        buffer.append(KEY_NAME + "=").append(ObjectName.quote(context.getName()));
215
216        return createObjectName(buffer);
217    }
218
219    @Override
220    public ObjectName getObjectNameForRouteController(CamelContext context) throws MalformedObjectNameException {
221        // prefer to use the given management name if previously assigned
222        String managementName = context.getManagementName();
223        if (managementName == null) {
224            managementName = context.getManagementNameStrategy().getName();
225        }
226
227        StringBuilder buffer = new StringBuilder();
228        buffer.append(domainName).append(":");
229        buffer.append(KEY_CONTEXT + "=").append(getContextId(managementName)).append(",");
230        buffer.append(KEY_TYPE + "=" + TYPE_ROUTE_CONTROLLER + ",");
231        buffer.append(KEY_NAME + "=").append(ObjectName.quote(context.getName()));
232
233        return createObjectName(buffer);
234    }
235
236    public ObjectName getObjectNameForEndpoint(Endpoint endpoint) throws MalformedObjectNameException {
237        StringBuilder buffer = new StringBuilder();
238        buffer.append(domainName).append(":");
239        buffer.append(KEY_CONTEXT + "=").append(getContextId(endpoint.getCamelContext())).append(",");
240        buffer.append(KEY_TYPE + "=" + TYPE_ENDPOINT + ",");
241        buffer.append(KEY_NAME + "=").append(ObjectName.quote(getEndpointId(endpoint)));
242        return createObjectName(buffer);
243    }
244
245    public ObjectName getObjectNameForDataFormat(CamelContext context, DataFormat dataFormat) throws MalformedObjectNameException {
246        StringBuilder buffer = new StringBuilder();
247        buffer.append(domainName).append(":");
248        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
249        buffer.append(KEY_TYPE + "=" + TYPE_DATAFORMAT + ",");
250        buffer.append(KEY_NAME + "=").append(dataFormat.getClass().getSimpleName());
251        if (!(dataFormat instanceof StaticService)) {
252            buffer.append("(").append(ObjectHelper.getIdentityHashCode(dataFormat)).append(")");
253        }
254        return createObjectName(buffer);
255    }
256
257    public ObjectName getObjectNameForComponent(Component component, String name) throws MalformedObjectNameException {
258        StringBuilder buffer = new StringBuilder();
259        buffer.append(domainName).append(":");
260        buffer.append(KEY_CONTEXT + "=").append(getContextId(component.getCamelContext())).append(",");
261        buffer.append(KEY_TYPE + "=" + TYPE_COMPONENT + ",");
262        buffer.append(KEY_NAME + "=").append(ObjectName.quote(name));
263        return createObjectName(buffer);
264    }
265
266    public ObjectName getObjectNameForProcessor(CamelContext context, Processor processor, NamedNode definition) throws MalformedObjectNameException {
267        StringBuilder buffer = new StringBuilder();
268        buffer.append(domainName).append(":");
269        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
270        buffer.append(KEY_TYPE + "=").append(TYPE_PROCESSOR).append(",");
271        buffer.append(KEY_NAME + "=").append(ObjectName.quote(definition.getId()));
272        return createObjectName(buffer);
273    }
274
275    public ObjectName getObjectNameForErrorHandler(RouteContext routeContext, Processor errorHandler, ErrorHandlerFactory builder) throws MalformedObjectNameException {
276        StringBuilder buffer = new StringBuilder();
277        buffer.append(domainName).append(":");
278        buffer.append(KEY_CONTEXT + "=").append(getContextId(routeContext.getCamelContext())).append(",");
279        buffer.append(KEY_TYPE + "=").append(TYPE_ERRORHANDLER + ",");
280
281        // we want to only register one instance of the various error handler types and thus do some lookup
282        // if its a ErrorHandlerBuildRef. We need a bit of work to do that as there are potential indirection.
283        String ref = null;
284        if (builder instanceof ErrorHandlerBuilderRef) {
285            ErrorHandlerBuilderRef builderRef = (ErrorHandlerBuilderRef) builder;
286
287            // it has not then its an indirection and we should do some work to lookup the real builder
288            ref = builderRef.getRef();
289            ErrorHandlerFactory refBuilder = ErrorHandlerBuilderRef.lookupErrorHandlerBuilder(routeContext, builderRef.getRef(), false);
290            if (refBuilder != null) {
291                builder = refBuilder;
292            }
293
294            // must do a 2nd lookup in case this is also a reference
295            // (this happens with spring DSL using errorHandlerRef on <route> as it gets a bit
296            // complex with indirections for error handler references
297            if (builder instanceof ErrorHandlerBuilderRef) {
298                builderRef = (ErrorHandlerBuilderRef) builder;
299                // does it refer to a non default error handler then do a 2nd lookup
300                if (!builderRef.getRef().equals(ErrorHandlerBuilderRef.DEFAULT_ERROR_HANDLER_BUILDER)) {
301                    refBuilder = ErrorHandlerBuilderRef.lookupErrorHandlerBuilder(routeContext, builderRef.getRef(), false);
302                    if (refBuilder != null) {
303                        ref = builderRef.getRef();
304                        builder = refBuilder;
305                    }
306                }
307            }
308        }
309
310        if (ref != null) {
311            String name = builder.getClass().getSimpleName() + "(ref:" + ref + ")";
312            buffer.append(KEY_NAME + "=").append(ObjectName.quote(name));
313        } else {
314            // create a name based on its instance
315            buffer.append(KEY_NAME + "=")
316                .append(builder.getClass().getSimpleName())
317                .append("(").append(ObjectHelper.getIdentityHashCode(builder)).append(")");
318        }
319
320        return createObjectName(buffer);
321    }
322
323    public ObjectName getObjectNameForConsumer(CamelContext context, Consumer consumer) throws MalformedObjectNameException {
324        StringBuilder buffer = new StringBuilder();
325        buffer.append(domainName).append(":");
326        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
327        buffer.append(KEY_TYPE + "=").append(TYPE_CONSUMER).append(",");
328
329        String name = consumer.getClass().getSimpleName();
330        if (ObjectHelper.isEmpty(name)) {
331            name = "Consumer";
332        }
333        buffer.append(KEY_NAME + "=")
334            .append(name)
335            .append("(").append(ObjectHelper.getIdentityHashCode(consumer)).append(")");
336        return createObjectName(buffer);
337    }
338
339    public ObjectName getObjectNameForProducer(CamelContext context, Producer producer) throws MalformedObjectNameException {
340        StringBuilder buffer = new StringBuilder();
341        buffer.append(domainName).append(":");
342        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
343        buffer.append(KEY_TYPE + "=").append(TYPE_PRODUCER).append(",");
344
345        String name = producer.getClass().getSimpleName();
346        if (ObjectHelper.isEmpty(name)) {
347            name = "Producer";
348        }
349        buffer.append(KEY_NAME + "=")
350            .append(name)
351            .append("(").append(ObjectHelper.getIdentityHashCode(producer)).append(")");
352        return createObjectName(buffer);
353    }
354
355    public ObjectName getObjectNameForTracer(CamelContext context, Service tracer) throws MalformedObjectNameException {
356        // use the simple name of the class as the mbean name (eg Tracer, BacklogTracer, BacklogDebugger)
357        String name = tracer.getClass().getSimpleName();
358
359        StringBuilder buffer = new StringBuilder();
360        buffer.append(domainName).append(":");
361        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
362        buffer.append(KEY_TYPE + "=" + TYPE_TRACER + ",");
363        buffer.append(KEY_NAME + "=").append(name);
364        return createObjectName(buffer);
365    }
366
367    public ObjectName getObjectNameForEventNotifier(CamelContext context, EventNotifier eventNotifier) throws MalformedObjectNameException {
368        StringBuilder buffer = new StringBuilder();
369        buffer.append(domainName).append(":");
370        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
371        buffer.append(KEY_TYPE + "=" + TYPE_EVENT_NOTIFIER + ",");
372
373        if (eventNotifier instanceof JmxNotificationEventNotifier) {
374            // JMX notifier shall have an easy to use name
375            buffer.append(KEY_NAME + "=").append("JmxEventNotifier");
376        } else {
377            // others can be per instance
378            buffer.append(KEY_NAME + "=")
379                .append("EventNotifier")
380                .append("(").append(ObjectHelper.getIdentityHashCode(eventNotifier)).append(")");
381        }
382        return createObjectName(buffer);
383    }
384
385    public ObjectName getObjectNameForRoute(Route route) throws MalformedObjectNameException {
386        Endpoint ep = route.getEndpoint();
387        String id = route.getId();
388
389        StringBuilder buffer = new StringBuilder();
390        buffer.append(domainName).append(":");
391        buffer.append(KEY_CONTEXT + "=").append(getContextId(ep.getCamelContext())).append(",");
392        buffer.append(KEY_TYPE + "=" + TYPE_ROUTE + ",");
393        buffer.append(KEY_NAME + "=").append(ObjectName.quote(id));
394        return createObjectName(buffer);
395    }
396
397    public ObjectName getObjectNameForService(CamelContext context, Service service) throws MalformedObjectNameException {
398        StringBuilder buffer = new StringBuilder();
399        buffer.append(domainName).append(":");
400        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
401        buffer.append(KEY_TYPE + "=" + TYPE_SERVICE + ",");
402        buffer.append(KEY_NAME + "=").append(service.getClass().getSimpleName());
403        if (!(service instanceof StaticService)) {
404            buffer.append("(").append(ObjectHelper.getIdentityHashCode(service)).append(")");
405        }
406        return createObjectName(buffer);
407    }
408
409    public ObjectName getObjectNameForClusterService(CamelContext context, CamelClusterService service) throws MalformedObjectNameException {
410        StringBuilder buffer = new StringBuilder();
411        buffer.append(domainName).append(":");
412        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
413        buffer.append(KEY_TYPE + "=" + TYPE_HA + ",");
414        buffer.append(KEY_NAME + "=").append(service.getClass().getSimpleName());
415        if (!(service instanceof StaticService)) {
416            buffer.append("(").append(ObjectHelper.getIdentityHashCode(service)).append(")");
417        }
418        return createObjectName(buffer);
419    }
420
421    public ObjectName getObjectNameForThreadPool(CamelContext context, ThreadPoolExecutor threadPool, String id, String sourceId) throws MalformedObjectNameException {
422        StringBuilder buffer = new StringBuilder();
423        buffer.append(domainName).append(":");
424        buffer.append(KEY_CONTEXT + "=").append(getContextId(context)).append(",");
425        buffer.append(KEY_TYPE + "=" + TYPE_THREAD_POOL + ",");
426
427        String name = id;
428        if (sourceId != null) {
429            // provide source id if we know it, this helps end user to know where the pool is used
430            name = name + "(" + sourceId + ")";
431        }
432        buffer.append(KEY_NAME + "=").append(ObjectName.quote(name));
433        return createObjectName(buffer);
434    }
435
436    public String getDomainName() {
437        return domainName;
438    }
439
440    public void setDomainName(String domainName) {
441        this.domainName = domainName;
442    }
443
444    public String getHostName() {
445        return hostName;
446    }
447
448    public void setHostName(String hostName) {
449        this.hostName = hostName;
450    }
451
452    protected String getContextId(CamelContext context) {
453        if (context == null) {
454            return getContextId(VALUE_UNKNOWN);
455        } else {
456            String name = context.getManagementName() != null ? context.getManagementName() : context.getName();
457            return getContextId(name);
458        }
459    }
460
461    protected String getContextId(String name) {
462        Boolean includeHostName = camelContext != null && camelContext.getManagementStrategy().getManagementAgent().getIncludeHostName();
463        if (includeHostName != null && includeHostName) {
464            return hostName + "/" + (name != null ? name : VALUE_UNKNOWN);
465        } else {
466            return name != null ? name : VALUE_UNKNOWN;
467        }
468    }
469
470    protected String getEndpointId(Endpoint ep) {
471        String answer = doGetEndpointId(ep);
472        Boolean sanitize = camelContext != null && camelContext.getManagementStrategy().getManagementAgent().getMask();
473        if (sanitize != null && sanitize) {
474            // use xxxxxx as replacements as * has to be quoted for MBean names
475            answer = URISupport.sanitizeUri(answer);
476        }
477        return answer;
478    }
479
480    private String doGetEndpointId(Endpoint ep) {
481        if (ep.isSingleton()) {
482            return ep.getEndpointKey();
483        } else {
484            // non singleton then add hashcoded id
485            String uri = ep.getEndpointKey();
486            int pos = uri.indexOf('?');
487            String id = (pos == -1) ? uri : uri.substring(0, pos);
488            id += "?id=" + ObjectHelper.getIdentityHashCode(ep);
489            return id;
490        }
491    }
492
493    /**
494     * Factory method to create an ObjectName escaping any required characters
495     */
496    protected ObjectName createObjectName(StringBuilder buffer) throws MalformedObjectNameException {
497        String text = buffer.toString();
498        try {
499            return new ObjectName(text);
500        } catch (MalformedObjectNameException e) {
501            throw new MalformedObjectNameException("Could not create ObjectName from: " + text + ". Reason: " + e);
502        }
503    }
504}