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.http.remote.RemoteMethod; 024import org.apache.juneau.httppart.*; 025import org.apache.juneau.httppart.bean.*; 026import org.apache.juneau.internal.*; 027import org.apache.juneau.reflect.*; 028 029/** 030 * Contains the meta-data about a Java method on a REST proxy class. 031 * 032 * <p> 033 * Captures the information in {@link RemoteMethod @RemoteMethod} annotations for caching and reuse. 034 * 035 * <ul class='seealso'> 036 * <li class='link'>{@doc RestcProxies} 037 * </ul> 038 */ 039public class RemoteMethodMeta { 040 041 private final String httpMethod; 042 private final String fullPath; 043 private final RemoteMethodArg[] pathArgs, queryArgs, headerArgs, formDataArgs; 044 private final RemoteMethodBeanArg[] requestArgs; 045 private final RemoteMethodArg bodyArg; 046 private final RemoteMethodReturn methodReturn; 047 private final Class<?>[] exceptions; 048 049 /** 050 * Constructor. 051 * 052 * @param parentPath The absolute URI of the REST interface backing the interface proxy. 053 * @param m The Java method. 054 * @param defaultMethod The default HTTP method if not specified through annotation. 055 */ 056 public RemoteMethodMeta(final String parentPath, Method m, String defaultMethod) { 057 Builder b = new Builder(parentPath, m, defaultMethod); 058 this.httpMethod = b.httpMethod; 059 this.fullPath = b.fullPath; 060 this.pathArgs = b.pathArgs.toArray(new RemoteMethodArg[b.pathArgs.size()]); 061 this.queryArgs = b.queryArgs.toArray(new RemoteMethodArg[b.queryArgs.size()]); 062 this.formDataArgs = b.formDataArgs.toArray(new RemoteMethodArg[b.formDataArgs.size()]); 063 this.headerArgs = b.headerArgs.toArray(new RemoteMethodArg[b.headerArgs.size()]); 064 this.requestArgs = b.requestArgs.toArray(new RemoteMethodBeanArg[b.requestArgs.size()]); 065 this.bodyArg = b.bodyArg; 066 this.methodReturn = b.methodReturn; 067 this.exceptions = m.getExceptionTypes(); 068 } 069 070 private static final class Builder { 071 String httpMethod, fullPath, path; 072 List<RemoteMethodArg> 073 pathArgs = new LinkedList<>(), 074 queryArgs = new LinkedList<>(), 075 headerArgs = new LinkedList<>(), 076 formDataArgs = new LinkedList<>(); 077 List<RemoteMethodBeanArg> 078 requestArgs = new LinkedList<>(); 079 RemoteMethodArg bodyArg; 080 RemoteMethodReturn methodReturn; 081 082 @SuppressWarnings("deprecation") 083 Builder(String parentPath, Method m, String defaultMethod) { 084 085 MethodInfo mi = MethodInfo.of(m); 086 087 org.apache.juneau.rest.client.remote.RemoteMethod orm = mi.getLastAnnotation(org.apache.juneau.rest.client.remote.RemoteMethod.class); 088 if (orm == null) 089 orm = mi.getResolvedReturnType().getLastAnnotation(org.apache.juneau.rest.client.remote.RemoteMethod.class); 090 RemoteMethod rm = mi.getLastAnnotation(RemoteMethod.class); 091 if (rm == null) 092 rm = mi.getResolvedReturnType().getLastAnnotation(RemoteMethod.class); 093 094 httpMethod = rm == null ? (orm == null ? "" : orm.method()) : rm.method(); 095 path = rm == null ? (orm == null ? "" : orm.path()) : rm.path(); 096 097 if (path.isEmpty()) { 098 path = HttpUtils.detectHttpPath(m, true); 099 } 100 if (httpMethod.isEmpty()) 101 httpMethod = HttpUtils.detectHttpMethod(m, true, defaultMethod); 102 103 path = trimSlashes(path); 104 105 if (! isOneOf(httpMethod, "DELETE", "GET", "POST", "PUT", "OPTIONS", "HEAD", "CONNECT", "TRACE", "PATCH")) 106 throw new RemoteMetadataException(m, 107 "Invalid value specified for @RemoteMethod(httpMethod) annotation. Valid values are [DELTE,GET,POST,PUT,OPTIONS,HEAD,CONNECT,TRACE,PATCH]."); 108 109 methodReturn = new RemoteMethodReturn(mi); 110 111 fullPath = path.indexOf("://") != -1 ? path : (parentPath.isEmpty() ? urlEncodePath(path) : (trimSlashes(parentPath) + '/' + urlEncodePath(path))); 112 113 for (ParamInfo mpi : mi.getParams()) { 114 RemoteMethodArg rma = RemoteMethodArg.create(mpi); 115 if (rma != null) { 116 HttpPartType pt = rma.getPartType(); 117 if (pt == HEADER) 118 headerArgs.add(rma); 119 else if (pt == QUERY) 120 queryArgs.add(rma); 121 else if (pt == FORMDATA) 122 formDataArgs.add(rma); 123 else if (pt == PATH) 124 pathArgs.add(rma); 125 else 126 bodyArg = rma; 127 } 128 RequestBeanMeta rmba = RequestBeanMeta.create(mpi, PropertyStore.DEFAULT); 129 if (rmba != null) { 130 requestArgs.add(new RemoteMethodBeanArg(mpi.getIndex(), rmba)); 131 } 132 } 133 } 134 } 135 136 /** 137 * Returns the value of the {@link RemoteMethod#method() @RemoteMethod(httpMethod)} annotation on this Java method. 138 * 139 * @return The value of the annotation, never <jk>null</jk>. 140 */ 141 public String getHttpMethod() { 142 return httpMethod; 143 } 144 145 /** 146 * Returns the absolute URI of the REST interface invoked by this Java method. 147 * 148 * @return The absolute URI of the REST interface, never <jk>null</jk>. 149 */ 150 public String getFullPath() { 151 return fullPath; 152 } 153 154 /** 155 * Returns the {@link Path @Path} annotated arguments on this Java method. 156 * 157 * @return A map of {@link Path#value() @Path(value)} names to zero-indexed argument indices. 158 */ 159 public RemoteMethodArg[] getPathArgs() { 160 return pathArgs; 161 } 162 163 /** 164 * Returns the {@link Query @Query} annotated arguments on this Java method. 165 * 166 * @return A map of {@link Query#value() @Query(value)} names to zero-indexed argument indices. 167 */ 168 public RemoteMethodArg[] getQueryArgs() { 169 return queryArgs; 170 } 171 172 /** 173 * Returns the {@link FormData @FormData} annotated arguments on this Java method. 174 * 175 * @return A map of {@link FormData#value() @FormData(value)} names to zero-indexed argument indices. 176 */ 177 public RemoteMethodArg[] getFormDataArgs() { 178 return formDataArgs; 179 } 180 181 /** 182 * Returns the {@link Header @Header} annotated arguments on this Java method. 183 * 184 * @return A map of {@link Header#value() @Header(value)} names to zero-indexed argument indices. 185 */ 186 public RemoteMethodArg[] getHeaderArgs() { 187 return headerArgs; 188 } 189 190 /** 191 * Returns the {@link Request @Request} annotated arguments on this Java method. 192 * 193 * @return A list of zero-indexed argument indices. 194 */ 195 public RemoteMethodBeanArg[] getRequestArgs() { 196 return requestArgs; 197 } 198 199 /** 200 * Returns the argument annotated with {@link Body @Body}. 201 * 202 * @return A index of the argument with the {@link Body @Body} annotation, or <jk>null</jk> if no argument exists. 203 */ 204 public RemoteMethodArg getBodyArg() { 205 return bodyArg; 206 } 207 208 /** 209 * Returns whether the method returns the HTTP response body or status code. 210 * 211 * @return Whether the method returns the HTTP response body or status code. 212 */ 213 public RemoteMethodReturn getReturns() { 214 return methodReturn; 215 } 216 217 /** 218 * Returns the exceptions thrown by this method. 219 * 220 * @return The exceptions thrown by this method. Never <jk>null</jk>. 221 */ 222 public Class<?>[] getExceptions() { 223 return exceptions; 224 } 225}