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