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.io.IOException;
020import java.net.URISyntaxException;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.SortedMap;
028
029import org.apache.camel.Component;
030import org.apache.camel.ComponentConfiguration;
031import org.apache.camel.Endpoint;
032import org.apache.camel.spi.EndpointCompleter;
033import org.apache.camel.util.ObjectHelper;
034import org.apache.camel.util.URISupport;
035import org.apache.camel.util.UnsafeUriCharactersEncoder;
036
037/**
038 * Useful base class for implementations of {@link ComponentConfiguration}
039 */
040public abstract class ComponentConfigurationSupport implements ComponentConfiguration {
041    protected final Component component;
042    private Map<String, Object> propertyValues = new HashMap<String, Object>();
043    private String baseUri;
044
045    public ComponentConfigurationSupport(Component component) {
046        this.component = component;
047    }
048
049    @Override
050    public Map<String, Object> getParameters() {
051        return Collections.unmodifiableMap(propertyValues);
052    }
053
054    @Override
055    public void setParameters(Map<String, Object> newValues) {
056        ObjectHelper.notNull(newValues, "propertyValues");
057        this.propertyValues.clear();
058        // lets validate each property as we set it
059        Set<Map.Entry<String, Object>> entries = newValues.entrySet();
060        for (Map.Entry<String, Object> entry : entries) {
061            setParameter(entry.getKey(), entry.getValue());
062        }
063    }
064
065    @Override
066    public Object getParameter(String name) {
067        validatePropertyName(name);
068        return propertyValues.get(name);
069    }
070
071    @Override
072    public void setParameter(String name, Object value) {
073        Object convertedValue = validatePropertyValue(name, value);
074        propertyValues.put(name, convertedValue);
075    }
076
077    /**
078     * Returns the base URI without any scheme or URI query parameters (property values)
079     */
080    @Override
081    public String getBaseUri() {
082        return baseUri;
083    }
084
085    @Override
086    public void setBaseUri(String baseUri) {
087        this.baseUri = baseUri;
088    }
089
090    @Override
091    public Endpoint createEndpoint() throws Exception {
092        String uri = getUriString();
093        return component.createEndpoint(uri);
094    }
095
096    /**
097     * Configures the properties on the given endpoint
098     */
099    @Override
100    public void configureEndpoint(Endpoint endpoint) {
101        Map<String, Object> map = getParameters();
102        if (map != null) {
103            Set<Map.Entry<String, Object>> entries = map.entrySet();
104            for (Map.Entry<String, Object> entry : entries) {
105                setEndpointParameter(endpoint, entry.getKey(), entry.getValue());
106            }
107        }
108        // TODO validate all the values are valid (e.g. mandatory)
109    }
110
111    @Override
112    public String getUriString() {
113        List<String> queryParams = new ArrayList<String>();
114        for (Map.Entry<String, Object> entry : getParameters().entrySet()) {
115            String key = entry.getKey();
116            Object value = entry.getValue();
117            // convert to "param=value" format here, order will be preserved
118            if (value instanceof List) {
119                for (Object item : (List<?>)value) {
120                    queryParams.add(key + "=" + UnsafeUriCharactersEncoder.encode(item.toString()));
121                }
122            } else {
123                queryParams.add(key + "=" + UnsafeUriCharactersEncoder.encode(value.toString()));
124            }
125        }
126        Collections.sort(queryParams);
127        StringBuilder builder = new StringBuilder();
128        String base = getBaseUri();
129        if (base != null) {
130            builder.append(base);
131        }
132        String separator = "?";
133        for (String entry : queryParams) {
134            builder.append(separator);
135            builder.append(entry);
136            separator = "&";
137        }
138        return builder.toString();
139    }
140
141    @Override
142    public void setUriString(String uri) throws URISyntaxException {
143        String path = uri;
144        int idx = path.indexOf('?');
145        Map<String, Object> newParameters = Collections.emptyMap();
146        if (idx >= 0) {
147            path = path.substring(0, idx);
148            String query = uri.substring(idx + 1);
149            newParameters = URISupport.parseQuery(query, true);
150        }
151        setBaseUri(path);
152        setParameters(newParameters);
153    }
154
155    @Override
156    public ParameterConfiguration getParameterConfiguration(String name) {
157        return getParameterConfigurationMap().get(name);
158    }
159
160    public List<String> completeEndpointPath(String completionText) {
161        if (component instanceof EndpointCompleter) {
162            EndpointCompleter completer = (EndpointCompleter) component;
163            return completer.completeEndpointPath(this, completionText);
164        }
165        return new ArrayList<String>();
166    }
167
168    public String createParameterJsonSchema() {
169        // favor loading the json schema from the built-time generated file
170        String defaultName = component.getCamelContext().resolveComponentDefaultName(component.getClass().getName());
171        if (defaultName != null) {
172            try {
173                return component.getCamelContext().getComponentParameterJsonSchema(defaultName);
174            } catch (IOException e) {
175                // ignore as we fallback to create the json at runtime
176            }
177        }
178
179        // fallback to resolving at runtime
180        SortedMap<String, ParameterConfiguration> map = getParameterConfigurationMap();
181        StringBuilder buffer = new StringBuilder("{\n  \"properties\": {");
182        boolean first = true;
183        for (Map.Entry<String, ParameterConfiguration> entry :  map.entrySet()) {
184            if (first) {
185                first = false;
186            } else {
187                buffer.append(",");
188            }
189            buffer.append("\n    ");
190            buffer.append(entry.getValue().toJson());
191        }
192        buffer.append("\n  }\n}\n");
193        return buffer.toString();
194    }
195
196    /**
197     * Allow implementations to validate whether a property name is valid
198     * and either throw an exception or log a warning of an unknown property being used
199     */
200    protected void validatePropertyName(String name) {
201    }
202
203    /**
204     * Allow implementations to validate whether a property name is valid
205     * and either throw an exception or log a warning of an unknown property being used
206     * and to convert the given value to the correct type before updating the value.
207     */
208    protected Object validatePropertyValue(String name, Object value) {
209        validatePropertyName(name);
210        return value;
211    }
212
213}