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     */
017    package org.apache.camel.management;
018    
019    import java.net.UnknownHostException;
020    import java.util.concurrent.ThreadPoolExecutor;
021    import javax.management.MalformedObjectNameException;
022    import javax.management.ObjectName;
023    
024    import org.apache.camel.CamelContext;
025    import org.apache.camel.CamelContextAware;
026    import org.apache.camel.Component;
027    import org.apache.camel.Consumer;
028    import org.apache.camel.Endpoint;
029    import org.apache.camel.ErrorHandlerFactory;
030    import org.apache.camel.NamedNode;
031    import org.apache.camel.Processor;
032    import org.apache.camel.Producer;
033    import org.apache.camel.Route;
034    import org.apache.camel.Service;
035    import org.apache.camel.StaticService;
036    import org.apache.camel.builder.ErrorHandlerBuilderRef;
037    import org.apache.camel.spi.EventNotifier;
038    import org.apache.camel.spi.InterceptStrategy;
039    import org.apache.camel.spi.ManagementNamingStrategy;
040    import org.apache.camel.spi.RouteContext;
041    import org.apache.camel.util.InetAddressUtil;
042    import org.apache.camel.util.ObjectHelper;
043    import org.apache.camel.util.URISupport;
044    
045    /**
046     * Naming strategy used when registering MBeans.
047     */
048    public 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    }