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