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.impl;
018
019import java.lang.reflect.Field;
020import java.util.SortedMap;
021import java.util.TreeMap;
022
023import org.apache.camel.CamelContext;
024import org.apache.camel.ComponentConfiguration;
025import org.apache.camel.Endpoint;
026import org.apache.camel.spi.UriParam;
027import org.apache.camel.spi.UriParams;
028import org.apache.camel.util.ObjectHelper;
029import org.apache.camel.util.ReflectionHelper;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * A component implementation for endpoints which are annotated with UriEndpoint to describe
035 * their configurable parameters via annotations
036 */
037public abstract class UriEndpointComponent extends DefaultComponent {
038    private static final Logger LOG = LoggerFactory.getLogger(UriEndpointComponent.class);
039
040    private Class<? extends Endpoint> endpointClass;
041    private SortedMap<String, ParameterConfiguration> parameterConfigurationMap;
042
043    public UriEndpointComponent(Class<? extends Endpoint> endpointClass) {
044        this.endpointClass = endpointClass;
045    }
046
047    public UriEndpointComponent(CamelContext context, Class<? extends Endpoint> endpointClass) {
048        super(context);
049        this.endpointClass = endpointClass;
050    }
051
052    /**
053     * To use a specific endpoint class, instead of what has been provided by the constructors.
054     *
055     * @param endpointClass the endpoint class to use
056     */
057    public void setEndpointClass(Class<? extends Endpoint> endpointClass) {
058        this.endpointClass = endpointClass;
059    }
060
061    @Override
062    public ComponentConfiguration createComponentConfiguration() {
063        return new UriComponentConfiguration(this);
064    }
065
066    /**
067     * Returns a newly created sorted map, indexed by name of all the parameter configurations
068     * of the given endpoint class using introspection for the various annotations like
069     * {@link org.apache.camel.spi.UriEndpoint}, {@link org.apache.camel.spi.UriParam}, {@link org.apache.camel.spi.UriParams}
070     */
071    public static SortedMap<String, ParameterConfiguration> createParameterConfigurationMap(
072            Class<? extends Endpoint> endpointClass) {
073        SortedMap<String, ParameterConfiguration> answer = new TreeMap<String, ParameterConfiguration>();
074        populateParameterConfigurationMap(answer, endpointClass, "");
075        return answer;
076    }
077
078    protected static void populateParameterConfigurationMap(
079            final SortedMap<String, ParameterConfiguration> parameterMap, Class<?> aClass,
080            final String prefix) {
081        ReflectionHelper.doWithFields(aClass, new ReflectionHelper.FieldCallback() {
082            @Override
083            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
084                UriParam uriParam = field.getAnnotation(UriParam.class);
085                if (uriParam != null) {
086                    String name = uriParam.name();
087                    if (ObjectHelper.isEmpty(name)) {
088                        name = field.getName();
089                    }
090                    String propertyName = prefix + name;
091
092                    // is the parameter a nested configuration object
093                    Class<?> fieldType = field.getType();
094                    UriParams uriParams = fieldType.getAnnotation(UriParams.class);
095                    if (uriParams != null) {
096                        String nestedPrefix = uriParams.prefix();
097                        if (nestedPrefix == null) {
098                            nestedPrefix = "";
099                        }
100                        nestedPrefix = (prefix + nestedPrefix).trim();
101                        populateParameterConfigurationMap(parameterMap, fieldType, nestedPrefix);
102                    } else {
103                        if (parameterMap.containsKey(propertyName)) {
104                            LOG.warn("Duplicate property name " + propertyName + " defined on field " + field);
105                        } else {
106                            parameterMap.put(propertyName,
107                                    ParameterConfiguration.newInstance(propertyName, field, uriParam));
108                        }
109                    }
110                }
111            }
112        });
113    }
114
115    public Class<? extends Endpoint> getEndpointClass() {
116        return endpointClass;
117    }
118
119    /**
120     * Returns the sorted map of all the URI query parameter names to their {@link ParameterConfiguration} objects
121     */
122    public SortedMap<String, ParameterConfiguration> getParameterConfigurationMap() {
123        if (parameterConfigurationMap == null) {
124            parameterConfigurationMap = createParameterConfigurationMap(getEndpointClass());
125        }
126        return new TreeMap<String, ParameterConfiguration>(parameterConfigurationMap);
127    }
128
129}