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.component; 018 019import java.lang.reflect.InvocationTargetException; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import org.apache.camel.CamelContext; 028import org.apache.camel.CamelException; 029import org.apache.camel.ComponentConfiguration; 030import org.apache.camel.Endpoint; 031import org.apache.camel.impl.UriEndpointComponent; 032import org.apache.camel.spi.EndpointCompleter; 033import org.apache.camel.util.IntrospectionSupport; 034import org.apache.camel.util.ObjectHelper; 035 036/** 037 * Abstract base class for API Component Camel {@link org.apache.camel.Component} classes. 038 */ 039public abstract class AbstractApiComponent<E extends Enum<E> & ApiName, T, S extends ApiCollection<E, T>> 040 extends UriEndpointComponent implements EndpointCompleter { 041 042 protected T configuration; 043 044 // API collection 045 protected final S collection; 046 047 // API name class 048 protected final Class<E> apiNameClass; 049 050 public AbstractApiComponent(Class<? extends Endpoint> endpointClass, 051 Class<E> apiNameClass, S collection) { 052 super(endpointClass); 053 this.collection = collection; 054 this.apiNameClass = apiNameClass; 055 } 056 057 public AbstractApiComponent(CamelContext context, Class<? extends Endpoint> endpointClass, 058 Class<E> apiNameClass, S collection) { 059 super(context, endpointClass); 060 this.collection = collection; 061 this.apiNameClass = apiNameClass; 062 } 063 064 protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { 065 // split remaining path to get API name and method 066 final String[] pathElements = remaining.split("/"); 067 String apiNameStr; 068 String methodName; 069 switch (pathElements.length) { 070 case 1: 071 apiNameStr = ""; 072 methodName = pathElements[0]; 073 break; 074 case 2: 075 apiNameStr = pathElements[0]; 076 methodName = pathElements[1]; 077 break; 078 default: 079 throw new CamelException("Invalid URI path [" + remaining 080 + "], must be of the format " + collection.getApiNames() + "/<operation-name>"); 081 } 082 083 try { 084 // get API enum from apiName string 085 final E apiName = getApiName(apiNameStr); 086 087 final T endpointConfiguration = createEndpointConfiguration(apiName); 088 final Endpoint endpoint = createEndpoint(uri, methodName, apiName, endpointConfiguration); 089 090 // set endpoint property inBody 091 setProperties(endpoint, parameters); 092 093 // configure endpoint properties and initialize state 094 endpoint.configureProperties(parameters); 095 096 return endpoint; 097 } catch (InvocationTargetException e) { 098 if (e.getCause() instanceof IllegalArgumentException) { 099 throw new CamelException("Invalid URI path prefix [" + remaining 100 + "], must be one of " + collection.getApiNames()); 101 } 102 throw e; 103 } 104 } 105 106 protected abstract E getApiName(String apiNameStr) throws IllegalArgumentException; 107 108 protected abstract Endpoint createEndpoint(String uri, String methodName, E apiName, T endpointConfiguration); 109 110 protected T createEndpointConfiguration(E name) throws Exception { 111 final Map<String, Object> componentProperties = new HashMap<String, Object>(); 112 // copy component configuration, if set 113 if (configuration != null) { 114 IntrospectionSupport.getProperties(configuration, componentProperties, null, false); 115 } 116 117 // create endpoint configuration with component properties 118 final T endpointConfiguration = collection.getEndpointConfiguration(name); 119 IntrospectionSupport.setProperties(endpointConfiguration, componentProperties); 120 return endpointConfiguration; 121 } 122 123 public T getConfiguration() { 124 return configuration; 125 } 126 127 public void setConfiguration(T configuration) { 128 this.configuration = configuration; 129 } 130 131 @Override 132 public List<String> completeEndpointPath(ComponentConfiguration configuration, String completionText) { 133 final List<String> result = new ArrayList<String>(); 134 135 final Set<String> apiNames = collection.getApiNames(); 136 boolean useDefaultName = apiNames.size() == 1 && apiNames.contains(""); 137 138 // check if there is an API name present 139 completionText = ObjectHelper.isEmpty(completionText) ? "" : completionText; 140 final int prefixEnd = completionText.indexOf('/'); 141 final int pathEnd = completionText.lastIndexOf('?'); 142 143 // empty or incomplete API prefix, and no options, add API names or method names if useDefaultName 144 final Map<E, ? extends ApiMethodHelper<? extends ApiMethod>> apiHelpers = collection.getApiHelpers(); 145 if (prefixEnd == -1 && pathEnd == -1) { 146 147 if (useDefaultName) { 148 149 // complete method names for default API 150 final Set<Class<? extends ApiMethod>> apiMethods = collection.getApiMethods().keySet(); 151 final Class<? extends ApiMethod> apiMethod = apiMethods.iterator().next(); 152 final ApiMethodHelper<? extends ApiMethod> helper = apiHelpers.values().iterator().next(); 153 getCompletedMethods(result, completionText, apiMethod, helper); 154 } else { 155 156 // complete API names 157 for (String name : apiNames) { 158 if (!name.isEmpty() || name.startsWith(completionText)) { 159 result.add(name); 160 } 161 } 162 } 163 164 // path with complete API name prefix, but no options 165 } else if (prefixEnd != -1 && pathEnd == -1) { 166 167 // complete method names for specified API 168 final E apiName = getApiNameOrNull(completionText.substring(0, prefixEnd)); 169 if (apiName != null) { 170 final ApiMethodHelper<? extends ApiMethod> helper = apiHelpers.get(apiName); 171 completionText = completionText.substring(prefixEnd + 1); 172 for (Map.Entry<Class<? extends ApiMethod>, E> entry : collection.getApiMethods().entrySet()) { 173 if (entry.getValue().equals(apiName)) { 174 getCompletedMethods(result, completionText, entry.getKey(), helper); 175 break; 176 } 177 } 178 } 179 180 // complete options 181 } else { 182 183 // get last option text 184 final int lastParam = completionText.lastIndexOf('&'); 185 String optionText; 186 if (lastParam != -1) { 187 optionText = completionText.substring(lastParam + 1); 188 } else { 189 optionText = completionText.substring(pathEnd); 190 } 191 192 String methodName = null; 193 ApiMethodHelper<? extends ApiMethod> helper = null; 194 if (useDefaultName) { 195 196 // get default endpoint configuration and helper 197 methodName = completionText.substring(0, pathEnd); 198 helper = apiHelpers.values().iterator().next(); 199 } else { 200 201 // get API name and method name, if they exist 202 final String[] pathElements = completionText.substring(0, pathEnd).split("/"); 203 if (pathElements.length == 2) { 204 final E apiName = getApiNameOrNull(pathElements[0]); 205 methodName = pathElements[1]; 206 helper = collection.getHelper(apiName); 207 } 208 } 209 if (helper != null && !ObjectHelper.isEmpty(methodName)) { 210 // get other options from configuration 211 Set<String> existingOptions = configuration.getParameters().keySet(); 212 // get all method options 213 try { 214 final List<Object> arguments = helper.getArguments(methodName); 215 final int nArgs = arguments.size(); 216 final Set<String> options = new HashSet<String>(); 217 for (int i = 1; i < nArgs; i += 2) { 218 options.add((String) arguments.get(i)); 219 } 220 options.removeAll(existingOptions); 221 222 // return matching options 223 for (String option : options) { 224 if (option.startsWith(optionText)) { 225 result.add(option); 226 } 227 } 228 } catch (IllegalArgumentException ignore) { 229 // thrown from getArguments() when no matching methods, 230 // return an empty result 231 } 232 } 233 } 234 235 return result; 236 } 237 238 // returns null instead of throwing IllegalArgumentException for invalid name 239 protected E getApiNameOrNull(String nameStr) { 240 try { 241 return getApiName(nameStr); 242 } catch (IllegalArgumentException ignore) { 243 return null; 244 } 245 } 246 247 protected void getCompletedMethods(List<String> result, String completionText, 248 Class<? extends ApiMethod> apiMethod, ApiMethodHelper<? extends ApiMethod> helper) { 249 // add potential method names 250 final ApiMethod[] methods = apiMethod.getEnumConstants(); 251 for (ApiMethod method : methods) { 252 final String name = method.getName(); 253 if (name.startsWith(completionText)) { 254 result.add(name); 255 } 256 } 257 // add potential aliases 258 final Map<String, Set<String>> aliases = helper.getAliases(); 259 for (String alias : aliases.keySet()) { 260 if (alias.startsWith(completionText)) { 261 result.add(alias); 262 } 263 } 264 } 265}