PageRenderTime 70ms CodeModel.GetById 43ms app.highlight 21ms RepoModel.GetById 2ms app.codeStats 0ms

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

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