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