001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest.client.remote; 014 015import static org.apache.juneau.internal.StringUtils.*; 016import static org.apache.juneau.httppart.HttpPartType.*; 017 018import java.lang.reflect.*; 019import java.util.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.http.annotation.*; 023import org.apache.juneau.httppart.*; 024import org.apache.juneau.httppart.bean.*; 025import org.apache.juneau.internal.*; 026 027/** 028 * Contains the meta-data about a Java method on a REST proxy class. 029 * 030 * <p> 031 * Captures the information in {@link RemoteMethod @RemoteMethod} annotations for caching and reuse. 032 * 033 * <h5 class='section'>See Also:</h5> 034 * <ul class='doctree'> 035 * <li class='link'>{@doc juneau-rest-client.RestProxies} 036 * </ul> 037 */ 038public class RemoteMethodMeta { 039 040 private final String httpMethod; 041 private final String fullPath, path; 042 private final RemoteMethodArg[] pathArgs, queryArgs, headerArgs, formDataArgs, otherArgs; 043 private final RemoteMethodBeanArg[] requestArgs; 044 private final RemoteMethodArg bodyArg; 045 private final RemoteMethodReturn methodReturn; 046 private final Method method; 047 048 /** 049 * Constructor. 050 * 051 * @param parentPath The absolute URL of the REST interface backing the interface proxy. 052 * @param m The Java method. 053 * @param useMethodSignatures If <jk>true</jk> then the default path for the method should be the full method signature. 054 * @param defaultMethod The default HTTP method if not specified through annotation. 055 */ 056 public RemoteMethodMeta(final String parentPath, Method m, boolean useMethodSignatures, String defaultMethod) { 057 Builder b = new Builder(parentPath, m, useMethodSignatures, defaultMethod); 058 this.method = m; 059 this.httpMethod = b.httpMethod; 060 this.path = b.path; 061 this.fullPath = b.fullPath; 062 this.pathArgs = b.pathArgs.toArray(new RemoteMethodArg[b.pathArgs.size()]); 063 this.queryArgs = b.queryArgs.toArray(new RemoteMethodArg[b.queryArgs.size()]); 064 this.formDataArgs = b.formDataArgs.toArray(new RemoteMethodArg[b.formDataArgs.size()]); 065 this.headerArgs = b.headerArgs.toArray(new RemoteMethodArg[b.headerArgs.size()]); 066 this.requestArgs = b.requestArgs.toArray(new RemoteMethodBeanArg[b.requestArgs.size()]); 067 this.otherArgs = b.otherArgs.toArray(new RemoteMethodArg[b.otherArgs.size()]); 068 this.bodyArg = b.bodyArg; 069 this.methodReturn = b.methodReturn; 070 } 071 072 private static final class Builder { 073 String httpMethod, fullPath, path; 074 List<RemoteMethodArg> 075 pathArgs = new LinkedList<>(), 076 queryArgs = new LinkedList<>(), 077 headerArgs = new LinkedList<>(), 078 formDataArgs = new LinkedList<>(), 079 otherArgs = new LinkedList<>(); 080 List<RemoteMethodBeanArg> 081 requestArgs = new LinkedList<>(); 082 RemoteMethodArg bodyArg; 083 RemoteMethodReturn methodReturn; 084 085 Builder(String parentPath, Method m, boolean useMethodSignatures, String defaultMethod) { 086 087 RemoteMethod rm = m.getAnnotation(RemoteMethod.class); 088 089 httpMethod = rm == null ? "" : rm.method(); 090 path = rm == null ? "" : rm.path(); 091 092 if (path.isEmpty()) { 093 path = HttpUtils.detectHttpPath(m, ! useMethodSignatures); 094 if (useMethodSignatures) 095 path += HttpUtils.getMethodArgsSignature(m, true); 096 } 097 if (httpMethod.isEmpty()) 098 httpMethod = HttpUtils.detectHttpMethod(m, ! useMethodSignatures, defaultMethod); 099 100 path = trimSlashes(path); 101 102 if (! isOneOf(httpMethod, "DELETE", "GET", "POST", "PUT", "OPTIONS", "HEAD", "CONNECT", "TRACE", "PATCH")) 103 throw new RemoteMetadataException(m, 104 "Invalid value specified for @RemoteMethod(httpMethod) annotation. Valid values are [DELTE,GET,POST,PUT]."); 105 106 methodReturn = new RemoteMethodReturn(m); 107 108 fullPath = path.indexOf("://") != -1 ? path : (parentPath.isEmpty() ? urlEncode(path) : (trimSlashes(parentPath) + '/' + urlEncode(path))); 109 110 for (int i = 0; i < m.getParameterTypes().length; i++) { 111 RemoteMethodArg rma = RemoteMethodArg.create(m, i); 112 boolean annotated = false; 113 if (rma != null) { 114 annotated = true; 115 HttpPartType pt = rma.getPartType(); 116 if (pt == HEADER) 117 headerArgs.add(rma); 118 else if (pt == QUERY) 119 queryArgs.add(rma); 120 else if (pt == FORMDATA) 121 formDataArgs.add(rma); 122 else if (pt == PATH) 123 pathArgs.add(rma); 124 else if (pt == BODY) 125 bodyArg = rma; 126 else 127 annotated = false; 128 } 129 RequestBeanMeta rmba = RequestBeanMeta.create(m, i, PropertyStore.DEFAULT); 130 if (rmba != null) { 131 annotated = true; 132 requestArgs.add(new RemoteMethodBeanArg(i, null, rmba)); 133 } 134 if (! annotated) { 135 otherArgs.add(new RemoteMethodArg(i, BODY, null)); 136 } 137 } 138 } 139 } 140 141 /** 142 * Returns the value of the {@link RemoteMethod#method() @RemoteMethod(httpMethod)} annotation on this Java method. 143 * 144 * @return The value of the annotation, never <jk>null</jk>. 145 */ 146 public String getHttpMethod() { 147 return httpMethod; 148 } 149 150 /** 151 * Returns the absolute URL of the REST interface invoked by this Java method. 152 * 153 * @return The absolute URL of the REST interface, never <jk>null</jk>. 154 */ 155 public String getFullPath() { 156 return fullPath; 157 } 158 159 /** 160 * Returns the {@link Path @Path} annotated arguments on this Java method. 161 * 162 * @return A map of {@link Path#value() @Path(value)} names to zero-indexed argument indices. 163 */ 164 public RemoteMethodArg[] getPathArgs() { 165 return pathArgs; 166 } 167 168 /** 169 * Returns the {@link Query @Query} annotated arguments on this Java method. 170 * 171 * @return A map of {@link Query#value() @Query(value)} names to zero-indexed argument indices. 172 */ 173 public RemoteMethodArg[] getQueryArgs() { 174 return queryArgs; 175 } 176 177 /** 178 * Returns the {@link FormData @FormData} annotated arguments on this Java method. 179 * 180 * @return A map of {@link FormData#value() @FormData(value)} names to zero-indexed argument indices. 181 */ 182 public RemoteMethodArg[] getFormDataArgs() { 183 return formDataArgs; 184 } 185 186 /** 187 * Returns the {@link Header @Header} annotated arguments on this Java method. 188 * 189 * @return A map of {@link Header#value() @Header(value)} names to zero-indexed argument indices. 190 */ 191 public RemoteMethodArg[] getHeaderArgs() { 192 return headerArgs; 193 } 194 195 /** 196 * Returns the {@link Request @Request} annotated arguments on this Java method. 197 * 198 * @return A list of zero-indexed argument indices. 199 */ 200 public RemoteMethodBeanArg[] getRequestArgs() { 201 return requestArgs; 202 } 203 204 /** 205 * Returns the remaining non-annotated arguments on this Java method. 206 * 207 * @return A list of zero-indexed argument indices. 208 */ 209 public RemoteMethodArg[] getOtherArgs() { 210 return otherArgs; 211 } 212 213 /** 214 * Returns the argument annotated with {@link Body @Body}. 215 * 216 * @return A index of the argument with the {@link Body @Body} annotation, or <jk>null</jk> if no argument exists. 217 */ 218 public RemoteMethodArg getBodyArg() { 219 return bodyArg; 220 } 221 222 /** 223 * Returns whether the method returns the HTTP response body or status code. 224 * 225 * @return Whether the method returns the HTTP response body or status code. 226 */ 227 public RemoteMethodReturn getReturns() { 228 return methodReturn; 229 } 230 231 /** 232 * Returns the HTTP path of this method. 233 * 234 * @return 235 * The HTTP path of this method relative to the parent interface. 236 * <br>Never <jk>null</jk>. 237 * <br>Never has leading or trailing slashes. 238 */ 239 public String getPath() { 240 return path; 241 } 242 243 /** 244 * Returns the underlying Java method that this metadata is about. 245 * 246 * @return 247 * The underlying Java method that this metadata is about. 248 * <br>Never <jk>null</jk>. 249 */ 250 public Method getJavaMethod() { 251 return method; 252 } 253}