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.remote; 014 015import static org.apache.juneau.dto.html5.HtmlBuilder.*; 016import static org.apache.juneau.http.HttpMethodName.*; 017import static org.apache.juneau.internal.StringUtils.*; 018 019import java.io.*; 020import java.lang.reflect.*; 021import java.util.*; 022import java.util.Map; 023import java.util.concurrent.*; 024 025import org.apache.juneau.dto.*; 026import org.apache.juneau.dto.html5.*; 027import org.apache.juneau.http.*; 028import org.apache.juneau.http.annotation.Header; 029import org.apache.juneau.http.annotation.Path; 030import org.apache.juneau.internal.*; 031import org.apache.juneau.parser.*; 032import org.apache.juneau.remote.*; 033import org.apache.juneau.rest.*; 034import org.apache.juneau.rest.annotation.*; 035import org.apache.juneau.rest.exception.*; 036 037/** 038 * @deprecated Use {@link RrpcServlet} 039 * </ul> 040 */ 041@SuppressWarnings({"serial","javadoc"}) 042@Deprecated 043public abstract class RemoteInterfaceServlet extends BasicRestServlet { 044 045 private final Map<String,RemoteInterfaceMeta> serviceMap = new ConcurrentHashMap<>(); 046 047 //----------------------------------------------------------------------------------------------------------------- 048 // Abstract methods 049 //----------------------------------------------------------------------------------------------------------------- 050 051 /** 052 * Returns the list of interfaces to their implementation objects. 053 * 054 * <p> 055 * This class is called often and not cached, so any caching should occur in the subclass if necessary. 056 * 057 * @return The service map. 058 * @throws Exception 059 */ 060 protected abstract Map<Class<?>,Object> getServiceMap() throws Exception; 061 062 //----------------------------------------------------------------------------------------------------------------- 063 // REST methods 064 //----------------------------------------------------------------------------------------------------------------- 065 066 @RestMethod( 067 name=GET, 068 path="/", 069 summary="List of available remote interfaces", 070 description="Shows a list of the interfaces registered with this remote interface servlet." 071 ) 072 public List<LinkString> getInterfaces() throws Exception { 073 List<LinkString> l = new LinkedList<>(); 074 for (Class<?> c : getServiceMap().keySet()) 075 l.add(new LinkString(c.getName(), "servlet:/{0}", urlEncode(c.getName()))); 076 return l; 077 } 078 079 @RestMethod( 080 name=GET, 081 path="/{javaInterface}", 082 summary="List of available methods on interface", 083 description="Shows a list of all the exposed methods on an interface.", 084 htmldoc=@HtmlDoc( 085 nav="<h5>Interface: $RP{javaInterface}</h5>" 086 ) 087 ) 088 public Collection<LinkString> listMethods( 089 @Path(name="javaInterface", description="Java interface name", example="com.foo.MyInterface") String javaInterface 090 ) throws Exception { 091 092 List<LinkString> l = new ArrayList<>(); 093 for (String s : getMethods(javaInterface).keySet()) 094 l.add(new LinkString(s, "servlet:/{0}/{1}", urlEncode(javaInterface), urlEncode(s))); 095 return l; 096 } 097 098 @RestMethod( 099 name=GET, 100 path="/{javaInterface}/{javaMethod}", 101 summary="Form entry for interface method call", 102 description="Shows a form entry page for executing a remote interface method.", 103 htmldoc=@HtmlDoc( 104 nav={ 105 "<h5>Interface: $RP{javaInterface}</h5>", 106 "<h5>Method: $RP{javaMethod}</h5>" 107 } 108 ) 109 ) 110 public Div showEntryForm( 111 @Path(name="javaInterface", description="Java interface name", example="com.foo.MyInterface") String javaInterface, 112 @Path(name="javaMethod", description="Java method name", example="myMethod") String javaMethod 113 ) throws NotFound, Exception { 114 115 // Find the method. 116 RemoteInterfaceMethod rmm = getMethods(javaInterface).get(javaMethod); 117 if (rmm == null) 118 throw new NotFound("Method not found"); 119 120 Table t = table(); 121 122 Type[] types = rmm.getJavaMethod().getGenericParameterTypes(); 123 if (types.length == 0) { 124 t.child(tr(td("No arguments").colspan(3).style("text-align:center"))); 125 } else { 126 t.child(tr(th("Index"),th("Type"),th("Value"))); 127 for (int i = 0; i < types.length; i++) { 128 String type = ClassUtils.toString(types[i]); 129 t.child(tr(td(i), td(type), td(input().name(String.valueOf(i)).type("text")))); 130 } 131 } 132 133 t.child( 134 tr( 135 td().colspan(3).style("text-align:right").children( 136 types.length == 0 ? null : button("reset", "Reset"), 137 button("button","Cancel").onclick("window.location.href='/'"), 138 button("submit", "Submit") 139 ) 140 ) 141 ); 142 143 return div(form().id("form").action("request:/").method(POST).children(t)); 144 } 145 146 @RestMethod( 147 name=POST, 148 path="/{javaInterface}/{javaMethod}", 149 summary="Invoke an interface method", 150 description="Invoke a Java method by passing in the arguments as an array of serialized objects.\nThe returned object is then serialized to the response.", 151 htmldoc=@HtmlDoc( 152 nav= { 153 "<h5>Interface: $RP{javaInterface}</h5>", 154 "<h5>Method: $RP{javaMethod}</h5>" 155 } 156 ), 157 swagger=@MethodSwagger( 158 parameters= { 159 "{", 160 "in: 'body',", 161 "description: 'Serialized array of Java objects',", 162 "schema: {", 163 "type': 'array'", 164 "},", 165 "x-examples: {", 166 "'application/json+lax': '[\\'foo\\', 123, true]'", 167 "}", 168 "}" 169 }, 170 responses= { 171 "200:{ description:'The return object serialized', schema:{type:'any'},'x-example':{foo:123} }", 172 } 173 ) 174 ) 175 public Object invoke( 176 Reader r, 177 ReaderParser p, 178 @Header("Content-Type") ContentType contentType, 179 @Path(name="javaInterface", description="Java interface name", example="com.foo.MyInterface") String javaInterface, 180 @Path(name="javaMethod", description="Java method name", example="myMethod") String javaMethod 181 ) throws UnsupportedMediaType, NotFound, Exception { 182 183 // Find the parser. 184 if (p == null) 185 throw new UnsupportedMediaType("Could not find parser for media type ''{0}''", contentType); 186 RemoteInterfaceMeta rim = getInterfaceClass(javaInterface); 187 188 // Find the service. 189 Object service = getServiceMap().get(rim.getJavaClass()); 190 if (service == null) 191 throw new NotFound("Service not found"); 192 193 // Find the method. 194 RemoteInterfaceMethod rmm = getMethods(javaInterface).get(javaMethod); 195 if (rmm == null) 196 throw new NotFound("Method not found"); 197 198 // Parse the args and invoke the method. 199 java.lang.reflect.Method m = rmm.getJavaMethod(); 200 Object[] params = p.parseArgs(r, m.getGenericParameterTypes()); 201 return m.invoke(service, params); 202 } 203 204 205 //----------------------------------------------------------------------------------------------------------------- 206 // Other methods 207 //----------------------------------------------------------------------------------------------------------------- 208 209 private Map<String,RemoteInterfaceMethod> getMethods(String javaInterface) throws Exception { 210 return getInterfaceClass(javaInterface).getMethodsByPath(); 211 } 212 213 /** 214 * Return the <code>Class</code> given it's name if it exists in the services map. 215 */ 216 private RemoteInterfaceMeta getInterfaceClass(String javaInterface) throws NotFound, Exception { 217 RemoteInterfaceMeta rm = serviceMap.get(javaInterface); 218 if (rm == null) { 219 for (Class<?> c : getServiceMap().keySet()) { 220 if (c.getName().equals(javaInterface)) { 221 rm = new RemoteInterfaceMeta(c, null); 222 serviceMap.put(javaInterface, rm); 223 return rm; 224 } 225 } 226 throw new NotFound("Interface class not found"); 227 } 228 return rm; 229 } 230}