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