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