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.util;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.net.URL;
022import java.util.Enumeration;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Locale;
027import java.util.Map;
028import java.util.Properties;
029import java.util.Set;
030import java.util.SortedMap;
031import java.util.StringTokenizer;
032import java.util.TreeMap;
033
034import org.apache.camel.CamelContext;
035import org.apache.camel.Component;
036import org.apache.camel.Endpoint;
037import org.apache.camel.Exchange;
038import org.apache.camel.NoSuchBeanException;
039import org.apache.camel.NoSuchEndpointException;
040import org.apache.camel.component.properties.PropertiesComponent;
041import org.apache.camel.model.FromDefinition;
042import org.apache.camel.model.ProcessorDefinition;
043import org.apache.camel.model.ProcessorDefinitionHelper;
044import org.apache.camel.model.RouteDefinition;
045import org.apache.camel.spi.ClassResolver;
046import org.apache.camel.spi.RouteStartupOrder;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050import static org.apache.camel.util.ObjectHelper.isEmpty;
051import static org.apache.camel.util.ObjectHelper.isNotEmpty;
052import static org.apache.camel.util.ObjectHelper.notNull;
053
054/**
055 * A number of helper methods
056 *
057 * @version 
058 */
059public final class CamelContextHelper {
060    public static final String COMPONENT_BASE = "META-INF/services/org/apache/camel/component/";
061    public static final String COMPONENT_DESCRIPTOR = "META-INF/services/org/apache/camel/component.properties";
062    public static final String COMPONENT_DOCUMENTATION_PREFIX = "org/apache/camel/component/";
063    public static final String MODEL_DESCRIPTOR = "META-INF/services/org/apache/camel/model.properties";
064    public static final String MODEL_DOCUMENTATION_PREFIX = "org/apache/camel/model/";
065
066    private static final Logger LOG = LoggerFactory.getLogger(CamelContextHelper.class);
067
068    /**
069     * Utility classes should not have a public constructor.
070     */
071    private CamelContextHelper() {
072    }
073
074    /**
075     * Returns the mandatory endpoint for the given URI or the
076     * {@link org.apache.camel.NoSuchEndpointException} is thrown
077     */
078    public static Endpoint getMandatoryEndpoint(CamelContext camelContext, String uri)
079        throws NoSuchEndpointException {
080        Endpoint endpoint = camelContext.getEndpoint(uri);
081        if (endpoint == null) {
082            throw new NoSuchEndpointException(uri);
083        } else {
084            return endpoint;
085        }
086    }
087
088    /**
089     * Returns the mandatory endpoint for the given URI and type or the
090     * {@link org.apache.camel.NoSuchEndpointException} is thrown
091     */
092    public static <T extends Endpoint> T getMandatoryEndpoint(CamelContext camelContext, String uri, Class<T> type) {
093        Endpoint endpoint = getMandatoryEndpoint(camelContext, uri);
094        return ObjectHelper.cast(type, endpoint);
095    }
096
097    /**
098     * Converts the given value to the requested type
099     */
100    public static <T> T convertTo(CamelContext context, Class<T> type, Object value) {
101        notNull(context, "camelContext");
102        return context.getTypeConverter().convertTo(type, value);
103    }
104
105    /**
106     * Tried to convert the given value to the requested type
107     */
108    public static <T> T tryConvertTo(CamelContext context, Class<T> type, Object value) {
109        notNull(context, "camelContext");
110        return context.getTypeConverter().tryConvertTo(type, value);
111    }
112
113    /**
114     * Converts the given value to the specified type throwing an {@link IllegalArgumentException}
115     * if the value could not be converted to a non null value
116     */
117    public static <T> T mandatoryConvertTo(CamelContext context, Class<T> type, Object value) {
118        T answer = convertTo(context, type, value);
119        if (answer == null) {
120            throw new IllegalArgumentException("Value " + value + " converted to " + type.getName() + " cannot be null");
121        }
122        return answer;
123    }
124
125    /**
126     * Creates a new instance of the given type using the {@link org.apache.camel.spi.Injector} on the given
127     * {@link CamelContext}
128     */
129    public static <T> T newInstance(CamelContext context, Class<T> beanType) {
130        return context.getInjector().newInstance(beanType);
131    }
132
133    /**
134     * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the
135     * {@link CamelContext}
136     */
137    public static Object lookup(CamelContext context, String name) {
138        return context.getRegistry().lookupByName(name);
139    }
140
141    /**
142     * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the
143     * {@link CamelContext}
144     */
145    public static <T> T lookup(CamelContext context, String name, Class<T> beanType) {
146        return context.getRegistry().lookupByNameAndType(name, beanType);
147    }
148
149    /**
150     * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the
151     * {@link CamelContext} and try to convert it to the given type.
152     */
153    public static <T> T lookupAndConvert(CamelContext context, String name, Class<T> beanType) {
154        return tryConvertTo(context, beanType, lookup(context, name));
155    }
156
157    /**
158     * Look up a bean of the give type in the {@link org.apache.camel.spi.Registry} on the
159     * {@link CamelContext} returning an instance if only one bean is present,
160     */
161    public static <T> T findByType(CamelContext camelContext, Class<T> type) {
162        Set<T> set = camelContext.getRegistry().findByType(type);
163        if (set.size() == 1) {
164            return set.iterator().next();
165        }
166
167        return null;
168    }
169
170    /**
171     * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the
172     * {@link CamelContext} or throws {@link NoSuchBeanException} if not found.
173     */
174    public static Object mandatoryLookup(CamelContext context, String name) {
175        Object answer = lookup(context, name);
176        if (answer == null) {
177            throw new NoSuchBeanException(name);
178        }
179        return answer;
180    }
181
182    /**
183     * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the
184     * {@link CamelContext} or throws NoSuchBeanException if not found.
185     */
186    public static <T> T mandatoryLookup(CamelContext context, String name, Class<T> beanType) {
187        T answer = lookup(context, name, beanType);
188        if (answer == null) {
189            throw new NoSuchBeanException(name, beanType.getName());
190        }
191        return answer;
192    }
193
194    /**
195     * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the
196     * {@link CamelContext} and convert it to the given type or throws NoSuchBeanException if not found.
197     */
198    public static <T> T mandatoryLookupAndConvert(CamelContext context, String name, Class<T> beanType) {
199        Object value = lookup(context, name);
200        if (value == null) {
201            throw new NoSuchBeanException(name, beanType.getName());
202        }
203        return convertTo(context, beanType, value);
204    }
205
206    /**
207     * Evaluates the @EndpointInject annotation using the given context
208     */
209    public static Endpoint getEndpointInjection(CamelContext camelContext, String uri, String ref, String injectionPointName, boolean mandatory) {
210        if (ObjectHelper.isNotEmpty(uri) && ObjectHelper.isNotEmpty(ref)) {
211            throw new IllegalArgumentException("Both uri and name is provided, only either one is allowed: uri=" + uri + ", ref=" + ref);
212        }
213
214        Endpoint endpoint;
215        if (isNotEmpty(uri)) {
216            endpoint = camelContext.getEndpoint(uri);
217        } else {
218            // if a ref is given then it should be possible to lookup
219            // otherwise we do not catch situations where there is a typo etc
220            if (isNotEmpty(ref)) {
221                endpoint = mandatoryLookup(camelContext, ref, Endpoint.class);
222            } else {
223                if (isEmpty(ref)) {
224                    ref = injectionPointName;
225                }
226                if (mandatory) {
227                    endpoint = mandatoryLookup(camelContext, ref, Endpoint.class);
228                } else {
229                    endpoint = lookup(camelContext, ref, Endpoint.class);
230                }
231            }
232        }
233        return endpoint;
234    }
235
236    /**
237     * Gets the maximum cache pool size.
238     * <p/>
239     * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_CACHE_POOL_SIZE}.
240     * If no property has been set, then it will fallback to return a size of 1000.
241     *
242     * @param camelContext the camel context
243     * @return the maximum cache size
244     * @throws IllegalArgumentException is thrown if the property is illegal
245     */
246    public static int getMaximumCachePoolSize(CamelContext camelContext) throws IllegalArgumentException {
247        if (camelContext != null) {
248            String s = camelContext.getGlobalOption(Exchange.MAXIMUM_CACHE_POOL_SIZE);
249            if (s != null) {
250                try {
251                    // we cannot use Camel type converters as they may not be ready this early
252                    Integer size = Integer.valueOf(s);
253                    if (size == null || size <= 0) {
254                        throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_CACHE_POOL_SIZE + " must be a positive number, was: " + s);
255                    }
256                    return size;
257                } catch (NumberFormatException e) {
258                    throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_CACHE_POOL_SIZE + " must be a positive number, was: " + s, e);
259                }
260            }
261        }
262
263        // 1000 is the default fallback
264        return 1000;
265    }
266
267    /**
268     * Gets the maximum endpoint cache size.
269     * <p/>
270     * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_ENDPOINT_CACHE_SIZE}.
271     * If no property has been set, then it will fallback to return a size of 1000.
272     *
273     * @param camelContext the camel context
274     * @return the maximum cache size
275     * @throws IllegalArgumentException is thrown if the property is illegal
276     */
277    public static int getMaximumEndpointCacheSize(CamelContext camelContext) throws IllegalArgumentException {
278        if (camelContext != null) {
279            String s = camelContext.getGlobalOption(Exchange.MAXIMUM_ENDPOINT_CACHE_SIZE);
280            if (s != null) {
281                // we cannot use Camel type converters as they may not be ready this early
282                try {
283                    Integer size = Integer.valueOf(s);
284                    if (size == null || size <= 0) {
285                        throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_ENDPOINT_CACHE_SIZE + " must be a positive number, was: " + s);
286                    }
287                    return size;
288                } catch (NumberFormatException e) {
289                    throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_ENDPOINT_CACHE_SIZE + " must be a positive number, was: " + s, e);
290                }
291            }
292        }
293
294        // 1000 is the default fallback
295        return 1000;
296    }
297
298    /**
299     * Gets the maximum simple cache size.
300     * <p/>
301     * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_SIMPLE_CACHE_SIZE}.
302     * If no property has been set, then it will fallback to return a size of 1000.
303     *
304     * @param camelContext the camel context
305     * @return the maximum cache size
306     * @throws IllegalArgumentException is thrown if the property is illegal
307     */
308    public static int getMaximumSimpleCacheSize(CamelContext camelContext) throws IllegalArgumentException {
309        if (camelContext != null) {
310            String s = camelContext.getGlobalOption(Exchange.MAXIMUM_SIMPLE_CACHE_SIZE);
311            if (s != null) {
312                // we cannot use Camel type converters as they may not be ready this early
313                try {
314                    Integer size = Integer.valueOf(s);
315                    if (size == null || size <= 0) {
316                        throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_SIMPLE_CACHE_SIZE + " must be a positive number, was: " + s);
317                    }
318                    return size;
319                } catch (NumberFormatException e) {
320                    throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_SIMPLE_CACHE_SIZE + " must be a positive number, was: " + s, e);
321                }
322            }
323        }
324
325        // 1000 is the default fallback
326        return 1000;
327    }
328
329    /**
330     * Gets the maximum transformer cache size.
331     * <p/>
332     * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_TRANSFORMER_CACHE_SIZE}.
333     * If no property has been set, then it will fallback to return a size of 1000.
334     *
335     * @param camelContext the camel context
336     * @return the maximum cache size
337     * @throws IllegalArgumentException is thrown if the property is illegal
338     */
339    public static int getMaximumTransformerCacheSize(CamelContext camelContext) throws IllegalArgumentException {
340        if (camelContext != null) {
341            String s = camelContext.getGlobalOption(Exchange.MAXIMUM_TRANSFORMER_CACHE_SIZE);
342            if (s != null) {
343                // we cannot use Camel type converters as they may not be ready this early
344                try {
345                    Integer size = Integer.valueOf(s);
346                    if (size == null || size <= 0) {
347                        throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_TRANSFORMER_CACHE_SIZE + " must be a positive number, was: " + s);
348                    }
349                    return size;
350                } catch (NumberFormatException e) {
351                    throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_TRANSFORMER_CACHE_SIZE + " must be a positive number, was: " + s, e);
352                }
353            }
354        }
355
356        // 1000 is the default fallback
357        return 1000;
358    }
359
360    /**
361     * Gets the maximum validator cache size.
362     * <p/>
363     * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_VALIDATOR_CACHE_SIZE}.
364     * If no property has been set, then it will fallback to return a size of 1000.
365     *
366     * @param camelContext the camel context
367     * @return the maximum cache size
368     * @throws IllegalArgumentException is thrown if the property is illegal
369     */
370    public static int getMaximumValidatorCacheSize(CamelContext camelContext) throws IllegalArgumentException {
371        if (camelContext != null) {
372            String s = camelContext.getGlobalOption(Exchange.MAXIMUM_VALIDATOR_CACHE_SIZE);
373            if (s != null) {
374                // we cannot use Camel type converters as they may not be ready this early
375                try {
376                    Integer size = Integer.valueOf(s);
377                    if (size == null || size <= 0) {
378                        throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_VALIDATOR_CACHE_SIZE + " must be a positive number, was: " + s);
379                    }
380                    return size;
381                } catch (NumberFormatException e) {
382                    throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_VALIDATOR_CACHE_SIZE + " must be a positive number, was: " + s, e);
383                }
384            }
385        }
386
387        // 1000 is the default fallback
388        return 1000;
389    }
390
391    /**
392     * Parses the given text and handling property placeholders as well
393     *
394     * @param camelContext the camel context
395     * @param text  the text
396     * @return the parsed text, or <tt>null</tt> if the text was <tt>null</tt>
397     * @throws Exception is thrown if illegal argument
398     */
399    public static String parseText(CamelContext camelContext, String text) throws Exception {
400        // ensure we support property placeholders
401        return camelContext.resolvePropertyPlaceholders(text);
402    }
403
404    /**
405     * Parses the given text and converts it to an Integer and handling property placeholders as well
406     *
407     * @param camelContext the camel context
408     * @param text  the text
409     * @return the integer vale, or <tt>null</tt> if the text was <tt>null</tt>
410     * @throws Exception is thrown if illegal argument or type conversion not possible
411     */
412    public static Integer parseInteger(CamelContext camelContext, String text) throws Exception {
413        // ensure we support property placeholders
414        String s = camelContext.resolvePropertyPlaceholders(text);
415        if (s != null) {
416            try {
417                return camelContext.getTypeConverter().mandatoryConvertTo(Integer.class, s);
418            } catch (NumberFormatException e) {
419                if (s.equals(text)) {
420                    throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e);
421                } else {
422                    throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e);
423                }
424            }
425        }
426        return null;
427    }
428
429    /**
430     * Parses the given text and converts it to an Long and handling property placeholders as well
431     *
432     * @param camelContext the camel context
433     * @param text  the text
434     * @return the long vale, or <tt>null</tt> if the text was <tt>null</tt>
435     * @throws Exception is thrown if illegal argument or type conversion not possible
436     */
437    public static Long parseLong(CamelContext camelContext, String text) throws Exception {
438        // ensure we support property placeholders
439        String s = camelContext.resolvePropertyPlaceholders(text);
440        if (s != null) {
441            try {
442                return camelContext.getTypeConverter().mandatoryConvertTo(Long.class, s);
443            } catch (NumberFormatException e) {
444                if (s.equals(text)) {
445                    throw new IllegalArgumentException("Error parsing [" + s + "] as a Long.", e);
446                } else {
447                    throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Long.", e);
448                }
449            }
450        }
451        return null;
452    }
453
454    /**
455     * Parses the given text and converts it to a Double and handling property placeholders as well
456     *
457     * @param camelContext the camel context
458     * @param text  the text
459     * @return the double vale, or <tt>null</tt> if the text was <tt>null</tt>
460     * @throws Exception is thrown if illegal argument or type conversion not possible
461     */
462    public static Double parseDouble(CamelContext camelContext, String text) throws Exception {
463        // ensure we support property placeholders
464        String s = camelContext.resolvePropertyPlaceholders(text);
465        if (s != null) {
466            try {
467                return camelContext.getTypeConverter().mandatoryConvertTo(Double.class, s);
468            } catch (NumberFormatException e) {
469                if (s.equals(text)) {
470                    throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e);
471                } else {
472                    throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e);
473                }
474            }
475        }
476        return null;
477    }
478
479    /**
480     * Parses the given text and converts it to an Boolean and handling property placeholders as well
481     *
482     * @param camelContext the camel context
483     * @param text  the text
484     * @return the boolean vale, or <tt>null</tt> if the text was <tt>null</tt>
485     * @throws Exception is thrown if illegal argument or type conversion not possible
486     */
487    public static Boolean parseBoolean(CamelContext camelContext, String text) throws Exception {
488        // ensure we support property placeholders
489        String s = camelContext.resolvePropertyPlaceholders(text);
490        if (s != null) {
491            s = s.trim().toLowerCase(Locale.ENGLISH);
492            if (s.equals("true") || s.equals("false")) {
493                return "true".equals(s) ? Boolean.TRUE : Boolean.FALSE;
494            } else {
495                if (s.equals(text)) {
496                    throw new IllegalArgumentException("Error parsing [" + s + "] as a Boolean.");
497                } else {
498                    throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Boolean.");
499                }
500            }
501        }
502        return null;
503    }
504
505    /**
506     * Finds all possible Components on the classpath, already registered in {@link org.apache.camel.CamelContext},
507     * and from the {@link org.apache.camel.spi.Registry}.
508     */
509    public static SortedMap<String, Properties> findComponents(CamelContext camelContext) throws LoadPropertiesException {
510        ClassResolver resolver = camelContext.getClassResolver();
511        LOG.debug("Finding all components using class resolver: {} -> {}", new Object[]{resolver});
512        Enumeration<URL> iter = resolver.loadAllResourcesAsURL(COMPONENT_DESCRIPTOR);
513        return findComponents(camelContext, iter);
514    }
515
516    public static SortedMap<String, Properties> findComponents(CamelContext camelContext, Enumeration<URL> componentDescriptionIter)
517        throws LoadPropertiesException {
518
519        SortedMap<String, Properties> map = new TreeMap<String, Properties>();
520        while (componentDescriptionIter != null && componentDescriptionIter.hasMoreElements()) {
521            URL url = componentDescriptionIter.nextElement();
522            LOG.trace("Finding components in url: {}", url);
523            try {
524                Properties properties = new Properties();
525                properties.load(url.openStream());
526                String names = properties.getProperty("components");
527                if (names != null) {
528                    StringTokenizer tok = new StringTokenizer(names);
529                    while (tok.hasMoreTokens()) {
530                        String name = tok.nextToken();
531
532                        // try to find the class name for this component
533                        String className = null;
534                        InputStream is = null;
535                        try {
536                            // now load the component name resource so we can grab its properties and the class name
537                            Enumeration<URL> urls = camelContext.getClassResolver().loadAllResourcesAsURL(COMPONENT_BASE + name);
538                            if (urls != null && urls.hasMoreElements()) {
539                                is = urls.nextElement().openStream();
540                            }
541                            if (is != null) {
542                                Properties compProperties = new Properties();
543                                compProperties.load(is);
544                                if (!compProperties.isEmpty()) {
545                                    className = compProperties.getProperty("class");
546                                }
547                            }
548                        } catch (Exception e) {
549                            // ignore
550                        } finally {
551                            IOHelper.close(is);
552                        }
553
554                        // inherit properties we loaded first, as it has maven details
555                        Properties prop = new Properties();
556                        prop.putAll(properties);
557                        if (camelContext.hasComponent(name) != null) {
558                            prop.put("component", camelContext.getComponent(name));
559                        }
560                        if (className != null) {
561                            prop.put("class", className);
562                        }
563                        prop.put("name", name);
564                        map.put(name, prop);
565                    }
566                }
567            } catch (IOException e) {
568                throw new LoadPropertiesException(url, e);
569            }
570        }
571
572        // lets see what other components are registered on camel context
573        List<String> names = camelContext.getComponentNames();
574        for (String name : names) {
575            if (!map.containsKey(name)) {
576                Component component = camelContext.getComponent(name);
577                if (component != null) {
578                    Properties properties = new Properties();
579                    properties.put("component", component);
580                    properties.put("class", component.getClass().getName());
581                    properties.put("name", name);
582                    // override default component if name clash
583                    map.put(name, properties);
584                }
585            }
586        }
587
588        // lets see what other components are in the registry
589        Map<String, Component> beanMap = camelContext.getRegistry().findByTypeWithName(Component.class);
590        Set<Map.Entry<String, Component>> entries = beanMap.entrySet();
591        for (Map.Entry<String, Component> entry : entries) {
592            String name = entry.getKey();
593            if (!map.containsKey(name)) {
594                Component component = entry.getValue();
595                if (component != null) {
596                    Properties properties = new Properties();
597                    properties.put("component", component);
598                    properties.put("class", component.getClass().getName());
599                    properties.put("name", name);
600                    map.put(name, properties);
601                }
602            }
603        }
604        return map;
605    }
606
607    /**
608     * Find information about all the EIPs from camel-core.
609     */
610    public static SortedMap<String, Properties> findEips(CamelContext camelContext) throws LoadPropertiesException {
611        SortedMap<String, Properties> answer = new TreeMap<String, Properties>();
612
613        ClassResolver resolver = camelContext.getClassResolver();
614        LOG.debug("Finding all EIPs using class resolver: {} -> {}", new Object[]{resolver});
615        URL url = resolver.loadResourceAsURL(MODEL_DESCRIPTOR);
616        if (url != null) {
617            InputStream is = null;
618            try {
619                is = url.openStream();
620                String all = IOHelper.loadText(is);
621                String[] lines = all.split("\n");
622                for (String line : lines) {
623                    if (line.startsWith("#")) {
624                        continue;
625                    }
626
627                    Properties prop = new Properties();
628                    prop.put("name", line);
629
630                    String description = null;
631                    String label = null;
632                    String javaType = null;
633                    String title = null;
634
635                    // enrich with more meta-data
636                    String json = camelContext.explainEipJson(line, false);
637                    if (json != null) {
638                        List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("model", json, false);
639
640                        for (Map<String, String> row : rows) {
641                            if (row.get("title") != null) {
642                                title = row.get("title");
643                            }
644                            if (row.get("description") != null) {
645                                description = row.get("description");
646                            }
647                            if (row.get("label") != null) {
648                                label = row.get("label");
649                            }
650                            if (row.get("javaType") != null) {
651                                javaType = row.get("javaType");
652                            }
653                        }
654                    }
655
656                    if (title != null) {
657                        prop.put("title", title);
658                    }
659                    if (description != null) {
660                        prop.put("description", description);
661                    }
662                    if (label != null) {
663                        prop.put("label", label);
664                    }
665                    if (javaType != null) {
666                        prop.put("class", javaType);
667                    }
668
669                    answer.put(line, prop);
670                }
671            } catch (IOException e) {
672                throw new LoadPropertiesException(url, e);
673            } finally {
674                IOHelper.close(is);
675            }
676        }
677
678        return answer;
679    }
680
681    /**
682     * Gets the route startup order for the given route id
683     *
684     * @param camelContext  the camel context
685     * @param routeId       the id of the route
686     * @return the startup order, or <tt>0</tt> if not possible to determine
687     */
688    public static int getRouteStartupOrder(CamelContext camelContext, String routeId) {
689        for (RouteStartupOrder order : camelContext.getRouteStartupOrder()) {
690            if (order.getRoute().getId().equals(routeId)) {
691                return order.getStartupOrder();
692            }
693        }
694        return 0;
695    }
696
697    /**
698     * Lookup the {@link org.apache.camel.component.properties.PropertiesComponent} from the {@link org.apache.camel.CamelContext}.
699     * <p/>
700     * @param camelContext the camel context
701     * @param autoCreate whether to automatic create a new default {@link org.apache.camel.component.properties.PropertiesComponent} if no custom component
702     *                   has been configured.
703     * @return the properties component, or <tt>null</tt> if none has been defined, and auto create is <tt>false</tt>.
704     */
705    public static Component lookupPropertiesComponent(CamelContext camelContext, boolean autoCreate) {
706        // no existing properties component so lookup and add as component if possible
707        PropertiesComponent answer = (PropertiesComponent) camelContext.hasComponent("properties");
708        if (answer == null) {
709            // lookup what is stored under properties, as it may not be the Camel properties component
710            Object found = camelContext.getRegistry().lookupByName("properties");
711            if (found instanceof PropertiesComponent) {
712                answer = (PropertiesComponent) found;
713                camelContext.addComponent("properties", answer);
714            }
715        }
716        if (answer == null && autoCreate) {
717            // create a default properties component to be used as there may be default values we can use
718            LOG.info("No existing PropertiesComponent has been configured, creating a new default PropertiesComponent with name: properties");
719            // do not auto create using getComponent as spring auto-wire by constructor causes a side effect
720            answer = new PropertiesComponent(true);
721            camelContext.addComponent("properties", answer);
722        }
723        return answer;
724    }
725
726    /**
727     * Checks if any of the Camel routes is using an EIP with the given name
728     *
729     * @param camelContext  the Camel context
730     * @param name          the name of the EIP
731     * @return <tt>true</tt> if in use, <tt>false</tt> if not
732     */
733    public static boolean isEipInUse(CamelContext camelContext, String name) {
734        for (RouteDefinition route : camelContext.getRouteDefinitions()) {
735            for (FromDefinition from : route.getInputs()) {
736                if (name.equals(from.getShortName())) {
737                    return true;
738                }
739            }
740            Iterator<ProcessorDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class);
741            while (it.hasNext()) {
742                ProcessorDefinition def = it.next();
743                if (name.equals(def.getShortName())) {
744                    return true;
745                }
746            }
747        }
748        return false;
749    }
750
751    /**
752     * Inspects the given object and resolves any property placeholders from its properties.
753     * <p/>
754     * This implementation will check all the getter/setter pairs on this instance and for all the values
755     * (which is a String type) will be property placeholder resolved.
756     *
757     * @param camelContext the Camel context
758     * @param target       the object that should have the properties (eg getter/setter) resolved
759     * @throws Exception is thrown if property placeholders was used and there was an error resolving them
760     * @see org.apache.camel.CamelContext#resolvePropertyPlaceholders(String)
761     * @see org.apache.camel.component.properties.PropertiesComponent
762     */
763    public static void resolvePropertyPlaceholders(CamelContext camelContext, Object target) throws Exception {
764        LOG.trace("Resolving property placeholders for: {}", target);
765
766        // find all getter/setter which we can use for property placeholders
767        Map<String, Object> properties = new HashMap<String, Object>();
768        IntrospectionSupport.getProperties(target, properties, null);
769
770        Map<String, Object> changedProperties = new HashMap<String, Object>();
771        if (!properties.isEmpty()) {
772            LOG.trace("There are {} properties on: {}", properties.size(), target);
773            // lookup and resolve properties for String based properties
774            for (Map.Entry<String, Object> entry : properties.entrySet()) {
775                // the name is always a String
776                String name = entry.getKey();
777                Object value = entry.getValue();
778                if (value instanceof String) {
779                    // value must be a String, as a String is the key for a property placeholder
780                    String text = (String) value;
781                    text = camelContext.resolvePropertyPlaceholders(text);
782                    if (text != value) {
783                        // invoke setter as the text has changed
784                        boolean changed = IntrospectionSupport.setProperty(camelContext.getTypeConverter(), target, name, text);
785                        if (!changed) {
786                            throw new IllegalArgumentException("No setter to set property: " + name + " to: " + text + " on: " + target);
787                        }
788                        changedProperties.put(name, value);
789                        if (LOG.isDebugEnabled()) {
790                            LOG.debug("Changed property [{}] from: {} to: {}", new Object[]{name, value, text});
791                        }
792                    }
793                }
794            }
795        }
796    }
797}