001: /*
002: * Copyright (c) 1998-2007 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Emil Ong
028: */
029:
030: package com.caucho.soa.rest;
031:
032: import com.caucho.jaxb.JAXBUtil;
033: import com.caucho.util.URLUtil;
034:
035: import javax.jws.WebMethod;
036: import javax.jws.WebParam;
037: import javax.xml.bind.JAXBContext;
038: import javax.xml.bind.JAXBException;
039: import javax.xml.bind.Marshaller;
040: import javax.xml.bind.Unmarshaller;
041: import java.io.InputStream;
042: import java.io.IOException;
043: import java.io.OutputStream;
044: import java.lang.annotation.Annotation;
045: import java.lang.reflect.InvocationHandler;
046: import java.lang.reflect.Method;
047: import java.net.HttpURLConnection;
048: import java.net.URL;
049: import java.net.URLConnection;
050: import java.util.ArrayList;
051: import java.util.HashMap;
052: import java.util.logging.Logger;
053:
054: public abstract class RestProxy implements InvocationHandler {
055: private static final Logger log = Logger.getLogger(RestProxy.class
056: .getName());
057:
058: private RestEncoding _defaultRestEncoding = RestEncoding.QUERY;
059: private String _url;
060: protected Class _api;
061:
062: public RestProxy(Class api, String url) {
063: _api = api;
064: _url = url;
065:
066: if (_api.isAnnotationPresent(RestService.class)) {
067: RestService restService = (RestService) _api
068: .getAnnotation(RestService.class);
069:
070: _defaultRestEncoding = restService.encoding();
071: }
072: }
073:
074: public Object invoke(Object proxy, Method method, Object[] args)
075: throws Throwable {
076: String httpMethod = "GET";
077:
078: if (method.isAnnotationPresent(Delete.class))
079: httpMethod = "DELETE";
080:
081: if (method.isAnnotationPresent(Get.class))
082: httpMethod = "GET";
083:
084: if (method.isAnnotationPresent(Post.class))
085: httpMethod = "POST";
086:
087: if (method.isAnnotationPresent(Put.class))
088: httpMethod = "PUT";
089:
090: if (method.isAnnotationPresent(Head.class))
091: httpMethod = "HEAD";
092:
093: // Check annotations
094: String methodName = method.getName();
095: RestEncoding restEncoding = _defaultRestEncoding;
096:
097: if (method.isAnnotationPresent(WebMethod.class)) {
098: WebMethod webMethod = (WebMethod) method
099: .getAnnotation(WebMethod.class);
100:
101: if (webMethod.operationName().length() > 0)
102: methodName = webMethod.operationName();
103: }
104:
105: if (method.isAnnotationPresent(RestMethod.class)) {
106: RestMethod restMethod = (RestMethod) method
107: .getAnnotation(RestMethod.class);
108:
109: if (restMethod.operationName().length() > 0)
110: methodName = restMethod.operationName();
111:
112: if (restMethod.encoding() != RestEncoding.UNSET)
113: restEncoding = restMethod.encoding();
114: }
115:
116: // Build the url
117:
118: StringBuilder urlBuilder = new StringBuilder(_url);
119: StringBuilder queryBuilder = new StringBuilder();
120:
121: switch (restEncoding) {
122: case PATH:
123: if (!_url.endsWith("/"))
124: urlBuilder.append("/");
125:
126: urlBuilder.append(methodName);
127: urlBuilder.append("/");
128: break;
129: case QUERY:
130: queryBuilder.append("method=");
131: queryBuilder.append(methodName);
132: break;
133: }
134:
135: ArrayList<Object> postValues = new ArrayList<Object>();
136: HashMap<String, String> headers = new HashMap<String, String>();
137:
138: if (args != null) {
139: Class[] parameterTypes = method.getParameterTypes();
140: Annotation[][] annotations = method
141: .getParameterAnnotations();
142:
143: for (int i = 0; i < parameterTypes.length; i++) {
144: RestParam.Source source = RestParam.Source.QUERY;
145: String key = "arg" + i;
146:
147: for (int j = 0; j < annotations[i].length; j++) {
148: if (annotations[i][j].annotationType().equals(
149: RestParam.class)) {
150: RestParam restParam = (RestParam) annotations[i][j];
151: source = restParam.source();
152: } else if (annotations[i][j].annotationType()
153: .equals(WebParam.class)) {
154: WebParam webParam = (WebParam) annotations[i][j];
155:
156: if (!"".equals(webParam.name()))
157: key = webParam.name();
158: }
159: }
160:
161: switch (source) {
162: case PATH:
163: urlBuilder.append(URLUtil.encodeURL(args[i]
164: .toString()));
165: urlBuilder.append('/');
166: break;
167: case QUERY:
168: if (queryBuilder.length() > 0)
169: queryBuilder.append('&');
170:
171: queryBuilder.append(URLUtil.encodeURL(key));
172: queryBuilder.append('=');
173: queryBuilder.append(URLUtil.encodeURL(args[i]
174: .toString()));
175: break;
176: case POST:
177: postValues.add(args[i]);
178: break;
179: case HEADER:
180: headers.put(key, args[i].toString());
181: break;
182: }
183: }
184: }
185:
186: if (queryBuilder.length() > 0) {
187: urlBuilder.append('?');
188: urlBuilder.append(queryBuilder);
189: }
190:
191: URL url = new URL(urlBuilder.toString());
192: URLConnection connection = url.openConnection();
193:
194: if (connection instanceof HttpURLConnection) {
195: HttpURLConnection httpConnection = (HttpURLConnection) connection;
196:
197: try {
198: httpConnection.setRequestMethod(httpMethod);
199: httpConnection.setDoInput(true);
200:
201: if (postValues.size() > 0) {
202: httpConnection.setDoOutput(true);
203:
204: OutputStream out = httpConnection.getOutputStream();
205:
206: writePostData(out, postValues);
207:
208: out.flush();
209: }
210:
211: int code = httpConnection.getResponseCode();
212:
213: if (code == 200) {
214: if (method.getReturnType() == null)
215: return null;
216:
217: return readResponse(httpConnection.getInputStream());
218: } else {
219: log.finer("request failed: "
220: + httpConnection.getResponseMessage());
221:
222: throw new RestException(httpConnection
223: .getResponseMessage());
224: }
225: } finally {
226: httpConnection.disconnect();
227: }
228: } else
229: throw new RestException();
230: }
231:
232: protected abstract void writePostData(OutputStream out,
233: ArrayList<Object> postValues) throws IOException,
234: RestException;
235:
236: protected abstract Object readResponse(InputStream in)
237: throws IOException, RestException;
238: }
|