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