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