PageRenderTime 49ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/gwtrpccommlayer/src/main/java/com/googlecode/gwtrpccommlayer/client/impl/GwtRpcClientSideProxy.java

https://code.google.com/p/gwtrpccommlayer/
Java | 629 lines | 381 code | 93 blank | 155 comment | 53 complexity | 4f8fb3b536537e544ce115a913a3eacc MD5 | raw file
  1. /*
  2. * Copyright 2010 Jeff McHugh (Segue Development LLC)
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  5. * in compliance with the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License
  10. * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  11. * or implied. See the License for the specific language governing permissions and limitations under
  12. * the License.
  13. */
  14. package com.googlecode.gwtrpccommlayer.client.impl;
  15. import com.google.gwt.user.client.rpc.AsyncCallback;
  16. import com.googlecode.gwtrpccommlayer.shared.GwtRpcCommLayerPojoConstants;
  17. import com.googlecode.gwtrpccommlayer.shared.GwtRpcCommLayerPojoRequest;
  18. import com.googlecode.gwtrpccommlayer.shared.GwtRpcCommLayerPojoResponse;
  19. import org.apache.http.Header;
  20. import org.apache.http.HttpEntity;
  21. import org.apache.http.HttpResponse;
  22. import org.apache.http.client.methods.HttpPost;
  23. import org.apache.http.cookie.Cookie;
  24. import org.apache.http.entity.InputStreamEntity;
  25. import org.apache.http.impl.client.DefaultHttpClient;
  26. import java.io.*;
  27. import java.lang.reflect.Constructor;
  28. import java.lang.reflect.Method;
  29. import java.net.URL;
  30. import java.util.HashMap;
  31. import java.util.List;
  32. /**
  33. * This class handles all the HTTP(S) client interaction with the GwtRpcCommLayer Servlet
  34. *
  35. * @author jeff mchugh
  36. *
  37. */
  38. public class GwtRpcClientSideProxy implements IGwtRpcClientSideProxy
  39. {
  40. static int DEFAULT_STANDARD_PORT = 80;
  41. static int DEFAULT_SECURE_PORT = 443;
  42. private HashMap<String,Cookie> cookies = null;
  43. private String scheme;
  44. private String host;
  45. private Integer port;
  46. private String path;
  47. private String queryString;
  48. private boolean showResponseHeaders = false;
  49. public GwtRpcClientSideProxy()
  50. {
  51. cookies = new HashMap<String, Cookie>();
  52. }
  53. public GwtRpcClientSideProxy(URL url)
  54. {
  55. this();
  56. setUrl(url);
  57. }
  58. boolean doesListIncludeGwtAsynchCallback(Class[] interfaces)
  59. {
  60. Class target = AsyncCallback.class;
  61. for (Class intf : interfaces)
  62. {
  63. if ( intf.getName().equals(target.getName()))
  64. {
  65. return true;
  66. }
  67. }
  68. return false;
  69. }
  70. Object invoke_asynchronousMode(Object proxy, Method method, Object[] args) throws Throwable
  71. {
  72. if ( args.length == 0 )
  73. {
  74. throw new RuntimeException("A minimum of (1) object is required for asynchronous mode");
  75. }
  76. AsyncCallback theCallback = (AsyncCallback) args[args.length-1];
  77. /*
  78. * Wrap the Method (and Method arguments)
  79. */
  80. GwtRpcCommLayerPojoRequest pojoRequest = new GwtRpcCommLayerPojoRequest();
  81. //pojoRequest.setMethodName(method.getName());
  82. pojoRequest.setMethod(method);
  83. if ( args != null )
  84. {
  85. for (Object object : args)
  86. {
  87. if ( object != theCallback )//the last object is always going to be the callback implementation
  88. {
  89. pojoRequest.getMethodParameters().add( (Serializable) object);
  90. }
  91. }
  92. }
  93. /*
  94. * This block now makes it possible to execute in a "asynchronous" mode
  95. */
  96. GwtRpcCommLayerPojoResponse pojoResp = null;
  97. try
  98. {
  99. pojoResp = executeRequest(pojoRequest);
  100. if ( pojoResp != null )
  101. {
  102. Object result = pojoResp.getResponseInstance();
  103. AsynchCallBackRunnable runnable = new AsynchCallBackRunnable(theCallback, result);
  104. Thread thread = new Thread(runnable);
  105. thread.start();
  106. return null;
  107. }
  108. else
  109. {
  110. throw new RuntimeException("No valid GwtRpcCommLayerPojoResponse returned. Check for http errors in log.");
  111. }
  112. }
  113. catch(Throwable caught)
  114. {
  115. AsynchCallBackRunnable runnable = new AsynchCallBackRunnable(theCallback, caught);
  116. Thread thread = new Thread(runnable);
  117. thread.start();
  118. return null;
  119. }
  120. }
  121. Object invoke_synchronousMode(Object proxy, Method method, Object[] args) throws Throwable
  122. {
  123. /*
  124. * Wrap the Method (and Method arguments)
  125. */
  126. GwtRpcCommLayerPojoRequest pojoRequest = new GwtRpcCommLayerPojoRequest();
  127. //pojoRequest.setMethodName(method.getName());
  128. pojoRequest.setMethod(method);
  129. if ( args != null )
  130. {
  131. for (Object object : args)
  132. {
  133. pojoRequest.getMethodParameters().add( (Serializable) object);
  134. }
  135. }
  136. /*
  137. * This is the original block of code that executed for the "synchronous" mode
  138. */
  139. GwtRpcCommLayerPojoResponse pojoResp = executeRequest(pojoRequest);
  140. if ( pojoResp != null )
  141. {
  142. return pojoResp.getResponseInstance();
  143. }
  144. else
  145. {
  146. throw new RuntimeException("No valid GwtRpcCommLayerPojoResponse returned. Check for http errors in log.");
  147. }
  148. }
  149. @Override
  150. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  151. {
  152. int mode = 0;//default to synchronous mode
  153. if ( args != null && args.length > 0 )
  154. {
  155. Object last = args[args.length-1];
  156. if ( doesListIncludeGwtAsynchCallback(last.getClass().getInterfaces()))
  157. {
  158. mode = 1;
  159. }
  160. }
  161. if ( mode == 0 )
  162. {
  163. return invoke_synchronousMode(proxy, method, args);
  164. }
  165. else
  166. {
  167. return invoke_asynchronousMode(proxy, method, args);
  168. }
  169. }
  170. /**
  171. * The URL that this client shall interact with
  172. */
  173. public void setUrl(URL url)
  174. {
  175. setScheme(url.getProtocol());
  176. setHost(url.getHost());
  177. setPort(url.getPort());
  178. setPath(url.getPath());
  179. setQueryString(url.getQuery());
  180. }
  181. /**
  182. * Set this to true for debugging
  183. */
  184. public void setShowResponseHeaders(boolean b)
  185. {
  186. this.showResponseHeaders = b;
  187. }
  188. /**
  189. * Main method that executes to make call to remote server
  190. * this method uses an HTTP POST method and can keep state if needed (using cookies)
  191. * @throws IOException
  192. */
  193. protected synchronized GwtRpcCommLayerPojoResponse executeRequest(GwtRpcCommLayerPojoRequest pojoRequest) throws IOException
  194. {
  195. /*
  196. * Create HTTP CLIENT
  197. */
  198. DefaultHttpClient httpclient = new DefaultHttpClient();
  199. /*
  200. * Add Cookies to the request
  201. * this enables a state-full set of transactions
  202. * to take place. While this is NOT required for the
  203. * GwtRpcCommLayer framework, the actually developer might have this
  204. * requirement embedded into his/her servlet(s) and thus
  205. * this makes to possible to communicate just like
  206. * a browser would
  207. *
  208. */
  209. if ( getCookies().size() > 0 )
  210. {
  211. for (Cookie cookie : getCookies().values())
  212. {
  213. httpclient.getCookieStore().addCookie(cookie);
  214. }
  215. }
  216. /*
  217. * SERIALZED THE POJO-REQUEST OBJECT INTO BYTES
  218. */
  219. byte[] pojoByteArray = serializeIntoBytes(pojoRequest);
  220. long length = pojoByteArray.length;
  221. ByteArrayInputStream in = new ByteArrayInputStream(pojoByteArray);
  222. InputStreamEntity reqEntity = new InputStreamEntity(in, length);
  223. reqEntity.setContentType("binary/octet-stream");
  224. reqEntity.setChunked(false);
  225. /*
  226. * CONSTRUCT THE URL
  227. */
  228. String url = createFullyQualifiedURL();
  229. /*
  230. * Create POST instance
  231. */
  232. HttpPost httppost = new HttpPost(url);
  233. httppost.setEntity(reqEntity);
  234. /*
  235. * Add the correct user-agent
  236. */
  237. httppost.addHeader(GwtRpcCommLayerPojoConstants.GWT_RPC_COMM_LAYER_CLIENT_KEY, GwtRpcCommLayerPojoConstants.GWT_RPC_COMM_LAYER_POJO_CLIENT);
  238. /*
  239. * Notify any last minute listener
  240. */
  241. onBeforeRequest(httppost);
  242. HttpResponse response = httpclient.execute(httppost);
  243. /*
  244. * Provide a call back for timing, error handling, etc
  245. */
  246. onAfterRequest(httppost, response);
  247. /*
  248. * Check for server error
  249. */
  250. if ( response.getStatusLine().getStatusCode() >= 400 && response.getStatusLine().getStatusCode() <= 599 )
  251. {
  252. onResponseError(response.getStatusLine().getStatusCode(), response);
  253. return null;
  254. }
  255. else if (response.getFirstHeader("Content-Type") != null && !response.getFirstHeader("Content-Type").getValue().equals("binary/octet-stream"))
  256. {
  257. onResponseError("Content-Type must be 'binary/octet-stream'");
  258. return null;
  259. }
  260. else
  261. {
  262. /*
  263. * Provide a call-back for examining the response headers
  264. */
  265. if ( isShowResponseHeaders() && response.getAllHeaders() != null )
  266. {
  267. dumpResponseHeaders(response.getAllHeaders());
  268. }
  269. /*
  270. * Provide a call-back for examining the response cookies
  271. */
  272. List<Cookie> cookies = httpclient.getCookieStore().getCookies();
  273. if ( cookies != null )
  274. {
  275. recordCookies(cookies);
  276. }
  277. HttpEntity resEntity = response.getEntity();
  278. byte[] respData = deserializeIntoBytes(resEntity);
  279. try
  280. {
  281. GwtRpcCommLayerPojoResponse pojoResp = createInstanceFromBytes(respData);
  282. return pojoResp;
  283. }
  284. catch(ClassNotFoundException e)
  285. {
  286. throw new IOException(e);
  287. }
  288. }
  289. }
  290. /*
  291. * ---------------- METHODS THAT CAN BE EXTENDED BY SUB-CLASSES -------------
  292. */
  293. protected String createFullyQualifiedURL()
  294. {
  295. /*
  296. * Configure the URL and PATH
  297. */
  298. StringBuffer buff = new StringBuffer();
  299. buff.append(getScheme());
  300. buff.append("://");
  301. buff.append(getHost());
  302. if ( getPort() != null && getPort() != DEFAULT_STANDARD_PORT && getPort() != DEFAULT_SECURE_PORT )
  303. {
  304. buff.append(":"+getPort());
  305. }
  306. buff.append(getPath());
  307. if ( getQueryString() != null )
  308. {
  309. buff.append("?");
  310. buff.append(getQueryString());
  311. }
  312. return buff.toString();
  313. }
  314. protected byte[] serializeIntoBytes(GwtRpcCommLayerPojoRequest pojoRequest) throws IOException
  315. {
  316. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  317. ObjectOutputStream objOut = new ObjectOutputStream(bos);
  318. objOut.writeObject(pojoRequest);
  319. objOut.flush();
  320. objOut.close();
  321. byte[] all = bos.toByteArray();
  322. return all;
  323. }
  324. protected byte[] deserializeIntoBytes(HttpEntity respEntity ) throws IOException
  325. {
  326. byte[] b = new byte[512];
  327. ByteArrayOutputStream buff = new ByteArrayOutputStream();
  328. InputStream respIn = respEntity.getContent();
  329. while(true)
  330. {
  331. int vv = respIn.read(b);
  332. if ( vv == -1 )
  333. {
  334. break;
  335. }
  336. buff.write(b, 0, vv);
  337. }
  338. return buff.toByteArray();
  339. }
  340. protected GwtRpcCommLayerPojoResponse createInstanceFromBytes(byte[] data) throws IOException, ClassNotFoundException
  341. {
  342. ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(data));
  343. Object instance = objIn.readObject();
  344. GwtRpcCommLayerPojoResponse pojoResp = (GwtRpcCommLayerPojoResponse) instance;
  345. return pojoResp;
  346. }
  347. protected void recordCookies(List<Cookie> cookies)
  348. {
  349. for (Cookie cookie : cookies)
  350. {
  351. getCookies().put(cookie.getName(), cookie);
  352. }
  353. }
  354. protected void dumpResponseHeaders(Header header[])
  355. {
  356. for (Header header2 : header)
  357. {
  358. System.out.println("" + header2.getName() + ":" + header2.getValue() );
  359. }
  360. }
  361. /**
  362. * Provides a call-back for future functionality. Will get called before the actual HTTP Request is executed
  363. * @param httppost
  364. */
  365. protected void onBeforeRequest(HttpPost httppost)
  366. {
  367. }
  368. /**
  369. * Provides a call-back for future functionality. Will get called after the response is returned
  370. * @param httppost
  371. * @param response
  372. */
  373. protected void onAfterRequest(HttpPost httppost, HttpResponse response)
  374. {
  375. }
  376. /**
  377. * Called in the event of an HTTP Response Error (400 through 599)
  378. * @param errorCode
  379. * @param response
  380. */
  381. protected void onResponseError(int errorCode, HttpResponse response)
  382. {
  383. System.out.println("HTTP_ERROR code=" + errorCode + " " + response.getStatusLine().getReasonPhrase() );
  384. }
  385. /**
  386. * Called in the event of an general error outside of the http protocol
  387. * @param error
  388. */
  389. protected void onResponseError(String error)
  390. {
  391. System.out.println("Response Error=" + error);
  392. }
  393. /*
  394. * ---------------------------- GETTER/SETTER METHODS ---------------------------
  395. */
  396. /**
  397. * @param cookies the cookies to set
  398. */
  399. public void setCookies(HashMap<String,Cookie> cookies)
  400. {
  401. this.cookies = cookies;
  402. }
  403. /**
  404. * @return the cookies
  405. */
  406. public HashMap<String,Cookie> getCookies()
  407. {
  408. return cookies;
  409. }
  410. /**
  411. * @param scheme the scheme to set
  412. */
  413. public void setScheme(String scheme)
  414. {
  415. this.scheme = scheme;
  416. }
  417. /**
  418. * @return the scheme
  419. */
  420. public String getScheme()
  421. {
  422. return scheme;
  423. }
  424. /**
  425. * @param host the host to set
  426. */
  427. public void setHost(String host)
  428. {
  429. this.host = host;
  430. }
  431. /**
  432. * @return the host
  433. */
  434. public String getHost()
  435. {
  436. return host;
  437. }
  438. /**
  439. * @param port the port to set
  440. */
  441. public void setPort(Integer port)
  442. {
  443. if (port.equals(-1))
  444. port = 80;
  445. this.port = port;
  446. }
  447. /**
  448. * @return the port
  449. */
  450. public Integer getPort()
  451. {
  452. return port;
  453. }
  454. /**
  455. * @param path the path to set
  456. */
  457. public void setPath(String path)
  458. {
  459. this.path = path;
  460. }
  461. /**
  462. * @return the path
  463. */
  464. public String getPath()
  465. {
  466. return path;
  467. }
  468. /**
  469. * @param queryString the queryString to set
  470. */
  471. public void setQueryString(String queryString)
  472. {
  473. this.queryString = queryString;
  474. }
  475. /**
  476. * @return the queryString
  477. */
  478. public String getQueryString()
  479. {
  480. return queryString;
  481. }
  482. /**
  483. * @return the showResponseHeaders
  484. */
  485. boolean isShowResponseHeaders()
  486. {
  487. return showResponseHeaders;
  488. }
  489. /*
  490. * This class is for use with the asynchronous mode
  491. */
  492. static class AsynchCallBackRunnable<T> implements Runnable
  493. {
  494. AsyncCallback<T> callback;
  495. Throwable caughtThrowable;
  496. T result;
  497. public AsynchCallBackRunnable(AsyncCallback<T> callback, T result)
  498. {
  499. this.callback = callback;
  500. this.result = result;
  501. }
  502. public AsynchCallBackRunnable(AsyncCallback<T> callback, Throwable caughtThrowable)
  503. {
  504. this.callback = callback;
  505. this.caughtThrowable = caughtThrowable;
  506. }
  507. public void run()
  508. {
  509. try
  510. {
  511. if ( this.caughtThrowable != null )
  512. {
  513. this.callback.onFailure(this.caughtThrowable);
  514. }
  515. else if ( this.result != null )
  516. {
  517. this.callback.onSuccess(this.result);
  518. }
  519. else
  520. {
  521. //we hope the client wanted a Void. may still be a bug.
  522. Constructor<Void> cv = Void.class.getDeclaredConstructor();
  523. cv.setAccessible(true);
  524. Void v = cv.newInstance();
  525. //nasty!
  526. this.callback.onSuccess((T) v);
  527. }
  528. }
  529. catch(Throwable caught)
  530. {
  531. System.err.println("Failure in Asynch dispatch thread. " + caught.toString() );
  532. caught.printStackTrace();
  533. }
  534. }
  535. }
  536. }