001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution, if
019: * any, must include the following acknowlegement:
020: * "This product includes software developed by the
021: * Caucho Technology (http://www.caucho.com/)."
022: * Alternately, this acknowlegement may appear in the software itself,
023: * if and wherever such third-party acknowlegements normally appear.
024: *
025: * 4. The names "Hessian", "Resin", and "Caucho" must not be used to
026: * endorse or promote products derived from this software without prior
027: * written permission. For written permission, please contact
028: * info@caucho.com.
029: *
030: * 5. Products derived from this software may not be called "Resin"
031: * nor may "Resin" appear in their names without prior written
032: * permission of Caucho Technology.
033: *
034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
038: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
039: * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
040: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
041: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
042: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
043: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
044: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045: *
046: * @author Scott Ferguson
047: */
048:
049: package com.caucho.hessian.client;
050:
051: import com.caucho.hessian.io.*;
052: import com.caucho.services.server.*;
053:
054: import java.io.*;
055: import java.util.logging.*;
056: import java.lang.reflect.InvocationHandler;
057: import java.lang.reflect.Method;
058: import java.lang.reflect.Proxy;
059: import java.util.WeakHashMap;
060: import java.net.HttpURLConnection;
061: import java.net.URL;
062: import java.net.URLConnection;
063:
064: /**
065: * Proxy implementation for Hessian clients. Applications will generally
066: * use HessianProxyFactory to create proxy clients.
067: */
068: public class HessianProxy implements InvocationHandler {
069: private static final Logger log = Logger
070: .getLogger(HessianProxy.class.getName());
071:
072: protected HessianProxyFactory _factory;
073: private WeakHashMap<Method, String> _mangleMap = new WeakHashMap<Method, String>();
074: private URL _url;
075:
076: /**
077: * Package protected constructor for factory
078: */
079: HessianProxy(HessianProxyFactory factory, URL url) {
080: _factory = factory;
081: _url = url;
082: }
083:
084: /**
085: * Protected constructor for subclassing
086: */
087: protected HessianProxy(URL url, HessianProxyFactory factory) {
088: _factory = factory;
089: _url = url;
090: }
091:
092: /**
093: * Returns the proxy's URL.
094: */
095: public URL getURL() {
096: return _url;
097: }
098:
099: /**
100: * Handles the object invocation.
101: *
102: * @param proxy the proxy object to invoke
103: * @param method the method to call
104: * @param args the arguments to the proxy object
105: */
106: public Object invoke(Object proxy, Method method, Object[] args)
107: throws Throwable {
108: String mangleName;
109:
110: synchronized (_mangleMap) {
111: mangleName = _mangleMap.get(method);
112: }
113:
114: if (mangleName == null) {
115: String methodName = method.getName();
116: Class[] params = method.getParameterTypes();
117:
118: // equals and hashCode are special cased
119: if (methodName.equals("equals") && params.length == 1
120: && params[0].equals(Object.class)) {
121: Object value = args[0];
122: if (value == null
123: || !Proxy.isProxyClass(value.getClass()))
124: return new Boolean(false);
125:
126: HessianProxy handler = (HessianProxy) Proxy
127: .getInvocationHandler(value);
128:
129: return new Boolean(_url.equals(handler.getURL()));
130: } else if (methodName.equals("hashCode")
131: && params.length == 0)
132: return new Integer(_url.hashCode());
133: else if (methodName.equals("getHessianType"))
134: return proxy.getClass().getInterfaces()[0].getName();
135: else if (methodName.equals("getHessianURL"))
136: return _url.toString();
137: else if (methodName.equals("toString")
138: && params.length == 0)
139: return "HessianProxy[" + _url + "]";
140:
141: if (!_factory.isOverloadEnabled())
142: mangleName = method.getName();
143: else
144: mangleName = mangleName(method);
145:
146: synchronized (_mangleMap) {
147: _mangleMap.put(method, mangleName);
148: }
149: }
150:
151: InputStream is = null;
152: URLConnection conn = null;
153: HttpURLConnection httpConn = null;
154:
155: try {
156: if (log.isLoggable(Level.FINER))
157: log
158: .finer("Hessian[" + _url + "] calling "
159: + mangleName);
160:
161: conn = sendRequest(mangleName, args);
162:
163: if (conn instanceof HttpURLConnection) {
164: httpConn = (HttpURLConnection) conn;
165: int code = 500;
166:
167: try {
168: code = httpConn.getResponseCode();
169: } catch (Exception e) {
170: }
171:
172: parseResponseHeaders(conn);
173:
174: if (code != 200) {
175: StringBuffer sb = new StringBuffer();
176: int ch;
177:
178: try {
179: is = httpConn.getInputStream();
180:
181: if (is != null) {
182: while ((ch = is.read()) >= 0)
183: sb.append((char) ch);
184:
185: is.close();
186: }
187:
188: is = httpConn.getErrorStream();
189: if (is != null) {
190: while ((ch = is.read()) >= 0)
191: sb.append((char) ch);
192: }
193: } catch (FileNotFoundException e) {
194: throw new HessianConnectionException(
195: "HessianProxy cannot connect to '"
196: + _url, e);
197: } catch (IOException e) {
198: if (is == null)
199: throw new HessianConnectionException(code
200: + ": " + e, e);
201: else
202: throw new HessianConnectionException(code
203: + ": " + sb, e);
204: }
205:
206: if (is != null)
207: is.close();
208:
209: throw new HessianConnectionException(code + ": "
210: + sb.toString());
211: }
212: }
213:
214: is = conn.getInputStream();
215:
216: if (log.isLoggable(Level.FINEST)) {
217: PrintWriter dbg = new PrintWriter(new LogWriter(log));
218: is = new HessianDebugInputStream(is, dbg);
219: }
220:
221: AbstractHessianInput in = _factory.getHessianInput(is);
222:
223: in.startReply();
224:
225: Object value = in.readObject(method.getReturnType());
226:
227: if (value instanceof InputStream) {
228: value = new ResultInputStream(httpConn, is, in,
229: (InputStream) value);
230: is = null;
231: httpConn = null;
232: } else
233: in.completeReply();
234:
235: return value;
236: } catch (HessianProtocolException e) {
237: throw new HessianRuntimeException(e);
238: } finally {
239: try {
240: if (is != null)
241: is.close();
242: } catch (Exception e) {
243: log.log(Level.FINE, e.toString(), e);
244: }
245:
246: try {
247: if (httpConn != null)
248: httpConn.disconnect();
249: } catch (Exception e) {
250: log.log(Level.FINE, e.toString(), e);
251: }
252: }
253: }
254:
255: protected String mangleName(Method method) {
256: Class[] param = method.getParameterTypes();
257:
258: if (param == null || param.length == 0)
259: return method.getName();
260: else
261: return AbstractSkeleton.mangleName(method, false);
262: }
263:
264: /**
265: * Method that allows subclasses to parse response headers such as cookies.
266: * Default implementation is empty.
267: * @param conn
268: */
269: protected void parseResponseHeaders(URLConnection conn) {
270:
271: }
272:
273: protected URLConnection sendRequest(String methodName, Object[] args)
274: throws IOException {
275: URLConnection conn = null;
276:
277: conn = _factory.openConnection(_url);
278:
279: // Used chunked mode when available, i.e. JDK 1.5.
280: if (_factory.isChunkedPost()
281: && conn instanceof HttpURLConnection) {
282: try {
283: HttpURLConnection httpConn = (HttpURLConnection) conn;
284:
285: httpConn.setChunkedStreamingMode(8 * 1024);
286: } catch (Throwable e) {
287: }
288: }
289:
290: addRequestHeaders(conn);
291:
292: OutputStream os = null;
293:
294: try {
295: os = conn.getOutputStream();
296: } catch (Exception e) {
297: throw new HessianRuntimeException(e);
298: }
299:
300: try {
301: if (log.isLoggable(Level.FINEST)) {
302: PrintWriter dbg = new PrintWriter(new LogWriter(log));
303: os = new HessianDebugOutputStream(os, dbg);
304: }
305:
306: AbstractHessianOutput out = _factory.getHessianOutput(os);
307:
308: out.call(methodName, args);
309: out.flush();
310:
311: return conn;
312: } catch (IOException e) {
313: if (conn instanceof HttpURLConnection)
314: ((HttpURLConnection) conn).disconnect();
315:
316: throw e;
317: } catch (RuntimeException e) {
318: if (conn instanceof HttpURLConnection)
319: ((HttpURLConnection) conn).disconnect();
320:
321: throw e;
322: }
323: }
324:
325: /**
326: * Method that allows subclasses to add request headers such as cookies.
327: * Default implementation is empty.
328: */
329: protected void addRequestHeaders(URLConnection conn) {
330:
331: }
332:
333: static class ResultInputStream extends InputStream {
334: private HttpURLConnection _conn;
335: private InputStream _connIs;
336: private AbstractHessianInput _in;
337: private InputStream _hessianIs;
338:
339: ResultInputStream(HttpURLConnection conn, InputStream is,
340: AbstractHessianInput in, InputStream hessianIs) {
341: _conn = conn;
342: _connIs = is;
343: _in = in;
344: _hessianIs = hessianIs;
345: }
346:
347: public int read() throws IOException {
348: if (_hessianIs != null) {
349: int value = _hessianIs.read();
350:
351: if (value < 0)
352: close();
353:
354: return value;
355: } else
356: return -1;
357: }
358:
359: public int read(byte[] buffer, int offset, int length)
360: throws IOException {
361: if (_hessianIs != null) {
362: int value = _hessianIs.read(buffer, offset, length);
363:
364: if (value < 0)
365: close();
366:
367: return value;
368: } else
369: return -1;
370: }
371:
372: public void close() throws IOException {
373: HttpURLConnection conn = _conn;
374: _conn = null;
375:
376: InputStream connIs = _connIs;
377: _connIs = null;
378:
379: AbstractHessianInput in = _in;
380: _in = null;
381:
382: InputStream hessianIs = _hessianIs;
383: _hessianIs = null;
384:
385: try {
386: if (hessianIs != null)
387: hessianIs.close();
388: } catch (Exception e) {
389: log.log(Level.FINE, e.toString(), e);
390: }
391:
392: try {
393: if (in != null) {
394: in.completeReply();
395: in.close();
396: }
397: } catch (Exception e) {
398: log.log(Level.FINE, e.toString(), e);
399: }
400:
401: try {
402: if (connIs != null) {
403: connIs.close();
404: }
405: } catch (Exception e) {
406: log.log(Level.FINE, e.toString(), e);
407: }
408:
409: try {
410: if (conn != null) {
411: conn.disconnect();
412: }
413: } catch (Exception e) {
414: log.log(Level.FINE, e.toString(), e);
415: }
416: }
417: }
418:
419: static class LogWriter extends Writer {
420: private Logger _log;
421: private Level _level = Level.FINEST;
422: private StringBuilder _sb = new StringBuilder();
423:
424: LogWriter(Logger log) {
425: _log = log;
426: }
427:
428: public void write(char ch) {
429: if (ch == '\n' && _sb.length() > 0) {
430: _log.fine(_sb.toString());
431: _sb.setLength(0);
432: } else
433: _sb.append((char) ch);
434: }
435:
436: public void write(char[] buffer, int offset, int length) {
437: for (int i = 0; i < length; i++) {
438: char ch = buffer[offset + i];
439:
440: if (ch == '\n' && _sb.length() > 0) {
441: _log.log(_level, _sb.toString());
442: _sb.setLength(0);
443: } else
444: _sb.append((char) ch);
445: }
446: }
447:
448: public void flush() {
449: }
450:
451: public void close() {
452: if (_sb.length() > 0)
453: _log.log(_level, _sb.toString());
454: }
455: }
456: }
|