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