1 29 30 package com.caucho.soa.rest; 31 32 import com.caucho.jaxb.JAXBUtil; 33 import com.caucho.server.util.CauchoSystem; 34 import com.caucho.soa.servlet.ProtocolServlet; 35 import com.caucho.vfs.Vfs; 36 import com.caucho.vfs.WriteStream; 37 import com.caucho.xml.stream.XMLStreamWriterImpl; 38 39 import javax.jws.WebMethod; 40 import javax.jws.WebParam; 41 import javax.jws.WebService; 42 import javax.servlet.GenericServlet ; 43 import javax.servlet.ServletException ; 44 import javax.servlet.ServletRequest ; 45 import javax.servlet.ServletResponse ; 46 import javax.servlet.http.HttpServletRequest ; 47 import javax.servlet.http.HttpServletResponse ; 48 import javax.xml.bind.JAXBContext; 49 import javax.xml.bind.Marshaller; 50 import javax.xml.bind.Unmarshaller; 51 import java.io.IOException ; 52 import java.io.InputStream ; 53 import java.io.OutputStream ; 54 import java.lang.annotation.Annotation ; 55 import java.lang.reflect.Method ; 56 import java.lang.reflect.Modifier ; 57 import java.util.ArrayList ; 58 import java.util.HashMap ; 59 import java.util.Map ; 60 import java.util.logging.Logger ; 61 62 65 public class RestProtocolServlet extends GenericServlet 66 implements ProtocolServlet 67 { 68 private static final Logger log = 69 Logger.getLogger(RestProtocolServlet.class.getName()); 70 71 public static final String DELETE = "DELETE"; 72 public static final String GET = "GET"; 73 public static final String HEAD = "HEAD"; 74 public static final String POST = "POST"; 75 public static final String PUT = "PUT"; 76 77 private Map <String ,Map <String ,Method>> _methods 78 = new HashMap <String ,Map <String ,Method>>(); 79 80 private HashMap <String ,Method> _defaultMethods 81 = new HashMap <String ,Method>(); 82 83 private String _jaxbPackages; 84 private ArrayList <Class > _jaxbClasses = new ArrayList <Class >(); 85 86 private JAXBContext _context = null; 87 88 private Object _service; 89 90 public RestProtocolServlet() 91 { 92 } 93 94 public void setService(Object service) 95 { 96 _service = service; 97 } 98 99 public void addJaxbPackage(String packageName) 100 { 101 if (_jaxbPackages != null) 102 _jaxbPackages = _jaxbPackages + ";" + packageName; 103 else 104 _jaxbPackages = packageName; 105 } 106 107 public void addJaxbClass(Class cl) 108 { 109 _jaxbClasses.add(cl); 110 } 111 112 public void init() 113 throws ServletException 114 { 115 try { 116 Class cl = _service.getClass(); 117 118 if (cl.isAnnotationPresent(WebService.class)) { 119 WebService webService 120 = (WebService) cl.getAnnotation(WebService.class); 121 122 String endpoint = webService.endpointInterface(); 123 124 if (endpoint != null && ! "".equals(endpoint)) 125 cl = CauchoSystem.loadClass(webService.endpointInterface()); 126 } 127 128 _methods.put(DELETE, new HashMap <String ,Method>()); 129 _methods.put(GET, new HashMap <String ,Method>()); 130 _methods.put(HEAD, new HashMap <String ,Method>()); 131 _methods.put(POST, new HashMap <String ,Method>()); 132 _methods.put(PUT, new HashMap <String ,Method>()); 133 134 ArrayList <Class > jaxbClasses = _jaxbClasses; 135 136 for (Method method : cl.getMethods()) { 137 if (method.getDeclaringClass().equals(Object .class)) 138 continue; 139 140 int modifiers = method.getModifiers(); 141 142 if (Modifier.isStatic(modifiers) 144 || Modifier.isFinal(modifiers) 145 || ! Modifier.isPublic(modifiers)) 146 continue; 147 148 String methodName = method.getName(); 149 150 if (method.isAnnotationPresent(WebMethod.class)) { 151 WebMethod webMethod = 152 (WebMethod) method.getAnnotation(WebMethod.class); 153 154 if (! "".equals(webMethod.operationName())) 155 methodName = webMethod.operationName(); 156 } 157 158 if (method.isAnnotationPresent(RestMethod.class)) { 159 RestMethod restMethod = 160 (RestMethod) method.getAnnotation(RestMethod.class); 161 162 if (! "".equals(restMethod.operationName())) 163 methodName = restMethod.operationName(); 164 } 165 166 boolean hasHTTPMethod = false; 167 168 if (method.isAnnotationPresent(Delete.class)) { 169 if (_methods.get(DELETE).containsKey(methodName)) { 170 throw new UnsupportedOperationException ("Overloaded method: " + 171 method.getName()); 172 } 173 174 _methods.get(DELETE).put(methodName, method); 175 176 hasHTTPMethod = true; 177 } 178 179 if (method.isAnnotationPresent(Get.class)) { 180 if (_methods.get(GET).containsKey(methodName)) { 181 throw new UnsupportedOperationException ("Overloaded method: " + 182 method.getName()); 183 } 184 185 _methods.get(GET).put(methodName, method); 186 187 hasHTTPMethod = true; 188 } 189 190 if (method.isAnnotationPresent(Post.class)) { 191 if (_methods.get(POST).containsKey(methodName)) { 192 throw new UnsupportedOperationException ("Overloaded method: " + 193 method.getName()); 194 } 195 196 _methods.get(POST).put(methodName, method); 197 198 hasHTTPMethod = true; 199 } 200 201 if (method.isAnnotationPresent(Put.class)) { 202 if (_methods.get(PUT).containsKey(methodName)) { 203 throw new UnsupportedOperationException ("Overloaded method: " + 204 method.getName()); 205 } 206 207 _methods.get(PUT).put(methodName, method); 208 209 hasHTTPMethod = true; 210 } 211 212 if (method.isAnnotationPresent(Head.class)) { 213 if (_methods.get(HEAD).containsKey(methodName)) { 214 throw new UnsupportedOperationException ("Overloaded method: " + 215 method.getName()); 216 } 217 218 _methods.get(HEAD).put(methodName, method); 219 220 hasHTTPMethod = true; 221 } 222 223 if (! hasHTTPMethod) { 224 if (_defaultMethods.containsKey(methodName)) { 225 throw new UnsupportedOperationException ("Overloaded method: " + 226 method.getName()); 227 } 228 229 _defaultMethods.put(methodName, method); 230 } 231 232 if (_context == null) 233 JAXBUtil.introspectMethod(method, jaxbClasses); 234 } 235 236 if (_context != null) { 237 } 238 else if (_jaxbPackages != null) { 239 _context = JAXBContext.newInstance(_jaxbPackages); 240 } 241 else { 242 Class [] classes = jaxbClasses.toArray(new Class [jaxbClasses.size()]); 243 _context = JAXBContext.newInstance(classes); 244 } 245 } catch (RuntimeException e) { 246 throw e; 247 } catch (Exception e) { 248 throw new ServletException (e); 249 } 250 } 251 252 public void service(ServletRequest request, ServletResponse response) 253 throws ServletException , IOException 254 { 255 HttpServletRequest req = (HttpServletRequest ) request; 256 HttpServletResponse res = (HttpServletResponse ) response; 257 258 Map <String ,String > queryArguments = new HashMap <String ,String >(); 259 260 if (req.getQueryString() != null) 261 queryToMap(req.getQueryString(), queryArguments); 262 263 String [] pathArguments = null; 264 265 if (req.getPathInfo() != null) { 266 String pathInfo = req.getPathInfo(); 267 268 int startPos = 0; 270 int endPos = pathInfo.length(); 271 272 if (pathInfo.length() > 0 && pathInfo.charAt(0) == '/') 273 startPos = 1; 274 275 if (pathInfo.length() > startPos && 276 pathInfo.charAt(pathInfo.length() - 1) == '/') 277 endPos = pathInfo.length() - 1; 278 279 pathInfo = pathInfo.substring(startPos, endPos); 280 281 pathArguments = pathInfo.split("/"); 282 283 if (pathArguments.length == 1 && pathArguments[0].length() == 0) 284 pathArguments = new String [0]; 285 } 286 else 287 pathArguments = new String [0]; 288 289 try { 290 invoke(_service, req.getMethod(), pathArguments, queryArguments, 291 req, req.getInputStream(), res.getOutputStream()); 292 } 293 catch (NoSuchMethodException e) { 294 res.sendError(HttpServletResponse.SC_BAD_REQUEST); 295 } 296 catch (Throwable e) { 297 throw new ServletException (e); 298 } 299 } 300 301 private static void queryToMap(String query, 302 Map <String ,String > queryArguments) 303 { 304 String [] entries = query.split("&"); 305 306 for (String entry : entries) { 307 if (entry.indexOf("=") < 0) 308 continue; 309 310 String [] nameValue = entry.split("=", 2); 311 312 queryArguments.put(nameValue[0], nameValue[1]); 313 } 314 } 315 316 private void invoke(Object object, 317 String httpMethod, 318 String [] pathArguments, 319 Map <String ,String > queryArguments, 320 HttpServletRequest req, 321 InputStream postData, 322 OutputStream out) 323 throws Throwable 324 { 325 int pathIndex = 0; 326 boolean pathMethod = false; 327 328 336 String methodName = queryArguments.get("method"); 337 338 if ((methodName == null) && (pathArguments.length > 0)) { 339 methodName = pathArguments[0]; 340 341 if (methodName != null) 342 pathMethod = true; 343 } 344 345 Method method = _methods.get(httpMethod).get(methodName); 348 349 if (method == null) 351 method = _defaultMethods.get(methodName); 352 353 if (method == null) { 355 method = _defaultMethods.get(null); 356 357 pathMethod = false; 358 } 359 360 if (method == null) 361 throw new NoSuchMethodException (methodName); 362 363 if (pathMethod) 364 pathIndex = 1; 365 366 ArrayList arguments = new ArrayList (); 368 369 Class [] parameterTypes = method.getParameterTypes(); 370 Annotation [][] annotations = method.getParameterAnnotations(); 371 372 for (int i = 0; i < parameterTypes.length; i++) { 373 RestParam.Source source = RestParam.Source.QUERY; 374 String key = "arg" + i; 375 376 for (int j = 0; j < annotations[i].length; j++) { 377 if (annotations[i][j].annotationType().equals(RestParam.class)) { 378 RestParam restParam = (RestParam) annotations[i][j]; 379 source = restParam.source(); 380 } 381 else if (annotations[i][j].annotationType().equals(WebParam.class)) { 382 WebParam webParam = (WebParam) annotations[i][j]; 383 384 if (! "".equals(webParam.name())) 385 key = webParam.name(); 386 } 387 } 388 389 switch (source) { 390 case PATH: 391 { 392 String arg = null; 393 394 if (pathIndex < pathArguments.length) 395 arg = pathArguments[pathIndex++]; 396 397 arguments.add(stringToType(parameterTypes[i], arg)); 398 } 400 break; 401 case QUERY: 402 arguments.add(stringToType(parameterTypes[i], 403 queryArguments.get(key))); 404 break; 405 case POST: 406 { 407 Unmarshaller unmarshaller = _context.createUnmarshaller(); 408 arguments.add(unmarshaller.unmarshal(postData)); 409 } 410 break; 411 case HEADER: 412 arguments.add(stringToType(parameterTypes[i], req.getHeader(key))); 413 break; 414 } 415 } 416 417 Object result = method.invoke(object, arguments.toArray()); 418 419 Marshaller marshaller = _context.createMarshaller(); 420 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); 421 422 WriteStream ws = Vfs.openWrite(out); 423 424 try { 425 XMLStreamWriterImpl writer = new XMLStreamWriterImpl(ws); 426 marshaller.marshal(result, writer); 427 } 428 finally { 429 ws.close(); 430 } 431 } 432 433 private static Object stringToType(Class type, String arg) 434 throws Throwable 435 { 436 if (arg == null) { 437 return null; 438 } 439 else if (type.equals(boolean.class)) { 440 return new Boolean (arg); 441 } 442 else if (type.equals(Boolean .class)) { 443 return new Boolean (arg); 444 } 445 else if (type.equals(byte.class)) { 446 return new Byte (arg); 447 } 448 else if (type.equals(Byte .class)) { 449 return new Byte (arg); 450 } 451 else if (type.equals(char.class)) { 452 if (arg.length() != 1) { 453 throw new IllegalArgumentException ("Cannot convert String to type " + 454 type.getName()); 455 } 456 457 return new Character (arg.charAt(0)); 458 } 459 else if (type.equals(Character .class)) { 460 if (arg.length() != 1) { 461 throw new IllegalArgumentException ("Cannot convert String to type " + 462 type.getName()); 463 } 464 465 return new Character (arg.charAt(0)); 466 } 467 else if (type.equals(double.class)) { 468 return new Double (arg); 469 } 470 else if (type.equals(Double .class)) { 471 return new Double (arg); 472 } 473 else if (type.equals(float.class)) { 474 return new Float (arg); 475 } 476 else if (type.equals(Float .class)) { 477 return new Float (arg); 478 } 479 else if (type.equals(int.class)) { 480 return new Integer (arg); 481 } 482 else if (type.equals(Integer .class)) { 483 return new Integer (arg); 484 } 485 else if (type.equals(long.class)) { 486 return new Long (arg); 487 } 488 else if (type.equals(Long .class)) { 489 return new Long (arg); 490 } 491 else if (type.equals(short.class)) { 492 return new Short (arg); 493 } 494 else if (type.equals(Short .class)) { 495 return new Short (arg); 496 } 497 else if (type.equals(String .class)) { 498 return arg; 499 } 500 else 501 throw new IllegalArgumentException ("Cannot convert String to type " + 502 type.getName()); 503 } 504 } 505 | Popular Tags |