2
* JBoss, Home of Professional Open Source
3
* Copyright 2005, JBoss Inc., and individual contributors as indicated
4
* by the @authors tag. See the copyright.txt in the distribution for a
5
* full listing of individual contributors.
7
* This is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU Lesser General Public License as
9
* published by the Free Software Foundation; either version 2.1 of
10
* the License, or (at your option) any later version.
12
* This software is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this software; if not, write to the Free
19
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23
package org.jboss.remoting.transport.http;
25
import org.jboss.logging.Logger;
26
import org.jboss.remoting.CannotConnectException;
27
import org.jboss.remoting.ConnectionFailedException;
28
import org.jboss.remoting.Home;
29
import org.jboss.remoting.InvocationRequest;
30
import org.jboss.remoting.InvocationResponse;
31
import org.jboss.remoting.InvokerLocator;
32
import org.jboss.remoting.RemoteClientInvoker;
33
import org.jboss.remoting.ServerInvoker;
34
import org.jboss.remoting.Version;
35
import org.jboss.remoting.marshal.MarshalFactory;
36
import org.jboss.remoting.marshal.Marshaller;
37
import org.jboss.remoting.marshal.UnMarshaller;
38
import org.jboss.remoting.marshal.VersionedMarshaller;
39
import org.jboss.remoting.marshal.VersionedUnMarshaller;
40
import org.jboss.remoting.marshal.compress.CompressingUnMarshaller;
41
import org.jboss.remoting.marshal.http.HTTPMarshaller;
42
import org.jboss.remoting.marshal.http.HTTPUnMarshaller;
43
import org.jboss.remoting.marshal.serializable.SerializableUnMarshaller;
44
import org.jboss.remoting.serialization.ClassLoaderUtility;
45
import org.jboss.remoting.transport.web.WebUtil;
46
import org.jboss.remoting.util.SecurityUtility;
47
import org.jboss.util.Base64;
48
import org.jboss.util.threadpool.BasicThreadPool;
49
import org.jboss.util.threadpool.BlockingMode;
50
import org.jboss.util.threadpool.RunnableTaskWrapper;
51
import org.jboss.util.threadpool.Task;
52
import org.jboss.util.threadpool.ThreadPool;
54
import java.io.EOFException;
55
import java.io.IOException;
56
import java.io.InputStream;
57
import java.io.OutputStream;
58
import java.lang.reflect.Constructor;
59
import java.lang.reflect.Field;
60
import java.lang.reflect.InvocationTargetException;
61
import java.lang.reflect.Method;
62
import java.net.HttpURLConnection;
63
import java.net.InetSocketAddress;
64
import java.net.SocketAddress;
65
import java.net.SocketTimeoutException;
67
import java.security.AccessController;
68
import java.security.PrivilegedActionException;
69
import java.security.PrivilegedExceptionAction;
70
import java.util.HashMap;
71
import java.util.Iterator;
72
import java.util.List;
77
* HTTP client invoker. Used for making http requests on http/servlet invoker.
79
* @author <a href="mailto:tom@jboss.org">Tom Elrod</a>
81
public class HTTPClientInvoker extends RemoteClientInvoker
84
* Key for the configuration map that determines the threadpool size for
85
* simulated timeouts when using jdk 1.4.
87
public static final String MAX_NUM_TIMEOUT_THREADS = "maxNumTimeoutThreads";
90
* Key for the configuration map that determines the queue size for simulated
91
* timeout threadpool when using jdk 1.4.
93
public static final String MAX_TIMEOUT_QUEUE_SIZE = "maxTimeoutQueueSize";
96
* Specifies the default number of work threads in the thread pool for
97
* simulating timeouts when using jdk 1.4.
99
public static final int MAX_NUM_TIMEOUT_THREADS_DEFAULT = 10;
102
* Specifies the number of attempts to get a functioning connection
103
* to the http server. Defaults to 1.
105
public static final String NUMBER_OF_CALL_ATTEMPTS = "numberOfCallAttempts";
108
* Specifies whether useHttpURLConnection(), upon receiving a null InputStream or ErrorStream,
109
* should call the UnMarshaller.
111
public static final String UNMARSHAL_NULL_STREAM = "unmarshalNullStream";
113
protected static final Logger log = Logger.getLogger(HTTPClientInvoker.class);
115
protected boolean noThrowOnError;
116
protected int numberOfCallAttempts = 1;
117
protected boolean unmarshalNullStream = true;
118
protected boolean useRemotingContentType = false;
120
private Object timeoutThreadPoolLock = new Object();
121
private ThreadPool timeoutThreadPool;
123
public HTTPClientInvoker(InvokerLocator locator)
126
configureParameters();
129
public HTTPClientInvoker(InvokerLocator locator, Map configuration)
131
super(locator, configuration);
132
configureParameters();
140
* @throws java.io.IOException
141
* @throws org.jboss.remoting.ConnectionFailedException
144
protected Object transport(String sessionId, final Object invocation, Map metadata,
145
final Marshaller marshaller, final UnMarshaller unmarshaller)
146
throws IOException, ConnectionFailedException
148
// need to check the url and make sure it compatible protocol
149
final String validatedUrl = validateURL(getLocator().getLocatorURI());
151
if (metadata == null)
153
metadata = new HashMap();
156
final HttpURLConnection conn = createURLConnection(validatedUrl, metadata);
158
int simulatedTimeout = getSimulatedTimeout(configuration, metadata, conn);
160
if (simulatedTimeout <= 0)
162
return makeInvocation(conn, validatedUrl, invocation, metadata, marshaller, unmarshaller, true);
166
if (log.isTraceEnabled()) log.trace("using simulated timeout: " + simulatedTimeout);
167
class Holder {public Object value;}
168
final Holder resultHolder = new Holder();
169
final Map finalMetadata = metadata;
171
Runnable r = new Runnable()
177
resultHolder.value = useHttpURLConnection(conn, invocation, finalMetadata, marshaller, unmarshaller);
178
if (log.isTraceEnabled()) log.trace("result: " + resultHolder.value);
182
resultHolder.value = e;
183
if (log.isTraceEnabled()) log.trace("exception: " + e);
188
// BasicThreadPool timeout mechanism depends on the interrupted status of
189
// the running thread.
190
Thread.interrupted();
192
ThreadPool pool = getTimeoutThreadPool();
193
WaitingTaskWrapper wrapper = new WaitingTaskWrapper(r, simulatedTimeout);
194
if (log.isTraceEnabled()) log.trace("starting task in thread pool");
195
pool.runTaskWrapper(wrapper);
196
if (log.isTraceEnabled()) log.trace("task finished in thread pool");
198
Object result = resultHolder.value;
201
if (log.isDebugEnabled()) log.debug("invocation timed out");
202
Exception cause = new SocketTimeoutException("timed out");
203
throw new CannotConnectException("Can not connect http client invoker.", cause);
205
else if (result instanceof IOException)
207
throw (IOException) result;
209
else if (result instanceof RuntimeException)
211
throw (RuntimeException) result;
215
if (log.isTraceEnabled()) log.trace("returning result: " + result);
221
protected Object makeInvocation(HttpURLConnection conn, String url, Object invocation,
222
Map metadata, Marshaller marshaller, UnMarshaller unmarshaller,
226
Throwable savedException = null;
228
for (int i = 0; i < numberOfCallAttempts; i++)
232
Object o = useHttpURLConnection(conn, invocation, metadata, marshaller, unmarshaller);
233
if (log.isTraceEnabled()) log.trace("result: " + o);
236
catch (CannotConnectException e)
238
savedException = e.getCause();
239
String suffix = (i < (numberOfCallAttempts - 1) ? ": will retry" : "");
240
log.debug("Cannot connect on attempt " + (i + 1) + suffix);
241
conn = createURLConnection(url, metadata);
244
getSimulatedTimeout(configuration, metadata, conn);
249
String msg = "Can not connect http client invoker after " + numberOfCallAttempts + " attempt(s)";
250
throw new CannotConnectException(msg, savedException);
254
private Object useHttpURLConnection(HttpURLConnection conn, Object invocation, Map metadata,
255
Marshaller marshaller, UnMarshaller unmarshaller) throws WebServerError
257
Object result = null;
258
int responseCode = -1;
262
setChunked(configuration, conn);
264
// check to see if basic auth required
265
String basicAuth = getBasicAuth(metadata);
266
if (basicAuth != null)
268
conn.setRequestProperty("Authorization", basicAuth);
271
// check for ping request and process it now and return
272
result = checkForLeasePing(conn, invocation, metadata);
279
// Get the request method type
280
boolean sendingData = true;
281
String type = "POST";
282
if (metadata != null)
284
type = (String) metadata.get("TYPE");
287
if ((!type.equals("POST") && !type.equals("PUT")))
297
else // need to check for content type and set metadata
299
metadata = new HashMap();
300
Map header = new HashMap();
301
header.put(HTTPMetadataConstants.CONTENTTYPE, WebUtil.getContentType(invocation));
302
metadata.put("HEADER", header);
304
// Set request headers
305
Map header = (Map) metadata.get("HEADER");
308
Set keys = header.keySet();
309
Iterator itr = keys.iterator();
310
while (itr.hasNext())
312
String key = (String) itr.next();
313
String value = (String) header.get(key);
314
log.debug("Setting request header with " + key + " : " + value);
315
conn.setRequestProperty(key, value);
320
conn.setRequestProperty(HTTPMetadataConstants.CONTENTTYPE, WebUtil.getContentType(invocation));
323
metadata.put(HTTPMetadataConstants.USE_REMOTING_CONTENT_TYPE, Boolean.toString(useRemotingContentType));
325
// set the remoting version
326
conn.setRequestProperty(HTTPMetadataConstants.REMOTING_VERSION_HEADER, new Integer(getVersion()).toString());
327
// set the user agent
328
conn.setRequestProperty(HTTPMetadataConstants.REMOTING_USER_AGENT, "JBossRemoting - " + Version.VERSION);
333
conn.setDoOutput(true);
334
conn.setDoInput(true);
335
conn.setRequestMethod(type);
337
if (invocation instanceof String)
339
conn.setRequestProperty(HTTPMetadataConstants.REMOTING_CONTENT_TYPE, HTTPMetadataConstants.REMOTING_CONTENT_TYPE_STRING);
343
conn.setRequestProperty(HTTPMetadataConstants.REMOTING_CONTENT_TYPE, HTTPMetadataConstants.REMOTING_CONTENT_TYPE_NON_STRING);
346
OutputStream stream = getOutputStream(conn);
347
if (marshaller instanceof VersionedMarshaller)
348
((VersionedMarshaller) marshaller).write(invocation, stream, getVersion());
350
marshaller.write(invocation, stream);
351
responseCode = getResponseCode(conn);
353
Map headers = conn.getHeaderFields();
354
if (metadata == null)
356
metadata = new HashMap();
359
// sometimes I get headers with "null" keys (I don't know who's fault is it), so I need
360
// to clean the header map, unless I want to get an NPE thrown by metadata.putAll()
363
for(Iterator i = headers.entrySet().iterator(); i.hasNext(); )
365
Map.Entry e = (Map.Entry)i.next();
366
if (e.getKey() != null)
368
metadata.put(e.getKey(), e.getValue());
373
String responseMessage = getResponseMessage(conn);
374
metadata.put(HTTPMetadataConstants.RESPONSE_CODE_MESSAGE, responseMessage);
375
metadata.put(HTTPMetadataConstants.RESPONSE_CODE, new Integer(responseCode));
376
metadata.put(HTTPMetadataConstants.RESPONSE_HEADERS, headers);
378
InputStream is = (responseCode < 400) ? conn.getInputStream() : conn.getErrorStream();
379
if (is != null || unmarshalNullStream)
381
result = readResponse(metadata, headers, unmarshaller, is);
386
conn.setDoOutput(false);
387
conn.setDoInput(true);
388
conn.setRequestMethod(type);
392
InputStream is = (getResponseCode(conn) < 400) ? conn.getInputStream() : conn.getErrorStream();
393
Map headers = conn.getHeaderFields();
395
if (is != null || unmarshalNullStream)
397
result = readResponse(null, headers, unmarshaller, is);
400
if (metadata == null)
402
metadata = new HashMap();
404
metadata.putAll(headers);
405
String responseMessage = getResponseMessage(conn);
406
metadata.put(HTTPMetadataConstants.RESPONSE_CODE_MESSAGE, responseMessage);
407
responseCode = getResponseCode(conn);
408
metadata.put(HTTPMetadataConstants.RESPONSE_CODE, new Integer(responseCode));
409
metadata.put(HTTPMetadataConstants.RESPONSE_HEADERS, conn.getHeaderFields());
414
String message = "Can not connect http client invoker.";
415
if (e.getMessage() != null)
416
message += " " + e.getMessage() + ".";
420
String responseMessage = getResponseMessage(conn);
421
int code = getResponseCode(conn);
422
message += " Response: " + responseMessage + "/" + code + ".";
424
catch (IOException e1)
426
log.debug("Unable to retrieve response message", e1);
428
throw new CannotConnectException(message, e);
431
// now check for error response and throw exception unless configured to not do so
432
if(responseCode >= 400)
434
boolean doNotThrow = noThrowOnError;
437
Object configObj = metadata.get(HTTPMetadataConstants.NO_THROW_ON_ERROR);
438
if(configObj != null && configObj instanceof String)
440
doNotThrow = Boolean.valueOf((String)configObj).booleanValue();
446
if(result instanceof String)
448
// this is a html error page displayed by web server, need to conver to exception
449
WebServerError ex = new WebServerError((String)result);
452
else if (result instanceof InvocationResponse)
454
return ((InvocationResponse) result).getResult();
463
// if got here, wasn't configured to not throw exception, so will throw it.
465
// In this case, MicroRemoteClientInvoker will throw the exception carried by
466
// the InvocationResponse.
467
if (result instanceof InvocationResponse)
470
// Otherwise, create a new WebServerError.
471
if(result instanceof String)
473
WebServerError ex = new WebServerError((String)result);
478
WebServerError ex = new WebServerError("Error received when calling on web server. Error returned was " + responseCode);
487
private Object checkForLeasePing(HttpURLConnection conn, Object invocation, Map metadata) throws IOException
489
InvocationResponse response = null;
490
boolean shouldLease = false;
491
long leasePeriod = -1;
493
if(invocation != null && invocation instanceof InvocationRequest)
495
InvocationRequest request = (InvocationRequest)invocation;
497
Object payload = request.getParameter();
498
// although a bit of a hack, this will determin if first time ping called by client.
499
if(payload != null && payload instanceof String && "$PING$".equalsIgnoreCase((String)payload) && request.getReturnPayload() != null)
503
// now know is a ping request, so convert to be a HEAD method call
504
conn.setDoOutput(false);
505
conn.setDoInput(true);
506
conn.setRequestMethod("HEAD");
507
// set the remoting version
508
conn.setRequestProperty(HTTPMetadataConstants.REMOTING_VERSION_HEADER, new Integer(getVersion()).toString());
509
// set the user agent
510
conn.setRequestProperty(HTTPMetadataConstants.REMOTING_USER_AGENT, "JBossRemoting - " + Version.VERSION);
511
conn.setRequestProperty(HTTPMetadataConstants.REMOTING_LEASE_QUERY, "true");
512
conn.setRequestProperty("sessionId", request.getSessionId());
515
//InputStream is = (conn.getResponseCode() < 400) ? conn.getInputStream() : conn.getErrorStream();
516
Map headers = conn.getHeaderFields();
520
Object leasingEnabled = headers.get("LEASING_ENABLED");
521
if(leasingEnabled != null && leasingEnabled instanceof List)
523
shouldLease = new Boolean((String)((List)leasingEnabled).get(0)).booleanValue();
525
Object leasingPeriod = headers.get("LEASE_PERIOD");
526
if(leasingPeriod != null && leasingPeriod instanceof List)
528
leasePeriod = new Long((String)((List)leasingPeriod).get(0)).longValue();
532
catch (IOException e)
534
log.error("Error checking server for lease information.", e);
537
Map p = new HashMap();
538
p.put("clientLeasePeriod", new Long(leasePeriod));
539
InvocationResponse innterResponse = new InvocationResponse(null, new Boolean(shouldLease), false, p);
540
response = new InvocationResponse(null, innterResponse, false, null);
548
private Object readResponse(Map metadata, Map headers, UnMarshaller unmarshaller, InputStream is)
549
throws ClassNotFoundException, IOException
551
Object result = null;
552
String encoding = null;
553
Object ceObj = headers.get("Content-Encoding");
556
if (ceObj instanceof List)
558
encoding = (String) ((List) ceObj).get(0);
561
if (encoding != null && encoding.indexOf("gzip") >= 0)
563
unmarshaller = new CompressingUnMarshaller(MarshalFactory.getUnMarshaller(SerializableUnMarshaller.DATATYPE));
566
Map map = metadata == null ? new HashMap(headers) : metadata;
568
// UnMarshaller may not be an HTTPUnMarshaller, in which case it
569
// can ignore this parameter.
570
if (map.get(HTTPUnMarshaller.PRESERVE_LINES) == null)
572
Object o = configuration.get(HTTPUnMarshaller.PRESERVE_LINES);
574
map.put(HTTPUnMarshaller.PRESERVE_LINES, o);
577
map.put(HTTPMetadataConstants.USE_REMOTING_CONTENT_TYPE, Boolean.toString(useRemotingContentType));
581
if (unmarshaller instanceof VersionedUnMarshaller)
582
result = ((VersionedUnMarshaller)unmarshaller).read(is, map, getVersion());
584
result = unmarshaller.read(is, map);
586
catch (ClassNotFoundException e)
590
catch (IOException e)
592
log.trace(this + " unable to read response", e);
595
throw new EOFException();
603
private void setChunked(Map metadata, final HttpURLConnection conn)
605
String chunkedValue = (String) metadata.get("chunkedLength");
606
if (chunkedValue != null && chunkedValue.length() > 0)
610
int chunkedLength = Integer.parseInt(chunkedValue);
613
* Since HTTPURLConnection in jdk 1.4 does NOT have a setChunkedStreamingMode() method and
614
* the one in jdk 1.5 does, will have to use reflection to see if it exists before trying to set it.
618
Class cl = conn.getClass();
619
Class[] paramTypes = new Class[] {int.class};
620
Method setChunkedLengthMethod = getMethod(cl, "setChunkedStreamingMode", paramTypes);
621
setChunkedLengthMethod.invoke(conn, new Object[]{new Integer(chunkedLength)});
623
catch (NoSuchMethodException e)
625
log.warn("Could not set chunked length (" + chunkedLength + ") on http client transport as method not available with JDK 1.4 (only JDK 1.5 or higher)");
627
catch (IllegalAccessException e)
629
log.error("Error setting http client connection chunked length.");
632
catch (InvocationTargetException e)
634
log.error("Error setting http client connection chunked length.");
640
log.error("Unexpected error setting http client connection chunked length.");
644
catch (NumberFormatException e)
646
log.error("Could not set chunked length for http client connection because value (" + chunkedValue + ") is not a number.");
654
private int getSimulatedTimeout(Map configuration, Map metadata, final HttpURLConnection conn)
657
String connectionTimeout = (String) configuration.get("timeout");
658
String invocationTimeout = (String) metadata.get("timeout");
660
if (invocationTimeout != null && invocationTimeout.length() > 0)
664
timeout = Integer.parseInt(invocationTimeout);
666
catch (NumberFormatException e)
668
log.error("Could not set timeout for current invocation because value (" + invocationTimeout + ") is not a number.");
672
if (timeout < 0 && connectionTimeout != null && connectionTimeout.length() > 0)
676
timeout = Integer.parseInt(connectionTimeout);
678
catch (NumberFormatException e)
680
log.error("Could not set timeout for http client connection because value (" + connectionTimeout + ") is not a number.");
688
* Since URLConnection in jdk 1.4 does NOT have a setConnectTimeout() method and
689
* the one in jdk 1.5 does, will have to use reflection to see if it exists before
694
Class cl = conn.getClass();
695
Class[] paramTypes = new Class[] {int.class};
696
Method setTimeoutMethod = getMethod(cl, "setConnectTimeout", paramTypes);
697
setTimeoutMethod.invoke(conn, new Object[]{new Integer(timeout)});
698
setTimeoutMethod = getMethod(cl, "setReadTimeout", paramTypes);
699
setTimeoutMethod.invoke(conn, new Object[]{new Integer(timeout)});
702
catch (NoSuchMethodException e)
704
log.debug("Using older JDK (prior to 1.5): will simulate timeout");
706
catch (IllegalAccessException e)
708
log.error("Error setting http client connection timeout.");
711
catch (InvocationTargetException e)
713
log.error("Error setting http client connection timeout.");
719
log.error("Unexpected error setting http client connection timeout.");
726
protected String validateURL(String url)
728
String validatedUrl = url;
730
if (validatedUrl.startsWith("servlet"))
732
// servlet:// is a valid protocol, but only in the remoting world, so need to convert to http
733
validatedUrl = "http" + validatedUrl.substring("servlet".length());
738
protected Home getUsableAddress()
740
InvokerLocator savedLocator = locator;
741
String protocol = savedLocator.getProtocol();
742
String path = savedLocator.getPath();
743
Map params = savedLocator.getParameters();
744
List homes = getConnectHomes();
746
Iterator it = homes.iterator();
752
home = (Home) it.next();
753
locator = new InvokerLocator(protocol, home.host, home.port, path, params);
754
invoke(new InvocationRequest(null, null, ServerInvoker.ECHO, null, null, null));
755
if (log.isTraceEnabled()) log.trace(this + " able to contact server at: " + home);
760
log.debug(this + " unable to contact server at: " + home);
764
locator = savedLocator;
771
protected HttpURLConnection createURLConnection(String url, Map metadata) throws IOException
773
URL externalURL = null;
774
HttpURLConnection httpURLConn = null;
776
// need to find out if need to use a proxy or not
777
String proxyHost = null;
778
String proxyportString = null;
781
if (metadata != null)
783
// first check the metadata as will have precedence
784
proxyHost = (String) metadata.get("http.proxyHost");
785
proxyportString = (String) metadata.get("http.proxyPort");
786
if (proxyportString != null && proxyportString.length() > 0)
790
proxyPort = Integer.parseInt(proxyportString);
792
catch (NumberFormatException e)
794
log.warn("Error converting proxy port specified (" + proxyportString + ") to a number.");
799
// now determin if going to use proxy or not
800
if (proxyHost != null)
802
externalURL = new URL(url);
805
* Since URL in jdk 1.4 does NOT have a openConnection(Proxy) method and
806
* the one in jdk 1.5 does, will have to use reflection to see if it exists before trying to set it.
810
final Class proxyClass = ClassLoaderUtility.loadClass("java.net.Proxy", HTTPClientInvoker.class);
811
InetSocketAddress proxyAddress = new InetSocketAddress(proxyHost, proxyPort);
812
Class[] decalredClasses = proxyClass.getDeclaredClasses();
813
Class proxyTypeClass = null;
814
for(int x = 0; x < decalredClasses.length; x++)
816
Class declaredClass = decalredClasses[x];
817
String className = declaredClass.getName();
818
if(className.endsWith("Type"))
820
proxyTypeClass = declaredClass;
824
Object proxyType = null;
825
Field[] fields = proxyTypeClass.getDeclaredFields();
826
for(int i = 0; i < fields.length; i++)
828
Field field = fields[i];
829
String fieldName = field.getName();
830
if(fieldName.endsWith("HTTP"))
832
proxyType = field.get(proxyTypeClass);
836
Constructor proxyConstructor = proxyClass.getConstructor(new Class[] {proxyTypeClass, SocketAddress.class});
837
Object proxy = proxyConstructor.newInstance(new Object[] {proxyType, proxyAddress});
838
Method openConnection = getMethod(URL.class, "openConnection", new Class[] {proxyClass});
839
httpURLConn = (HttpURLConnection)openConnection.invoke(externalURL, new Object[] {proxy});
843
log.error("Can not set proxy for http invocation (proxy host: " + proxyHost + ", proxy port: " + proxyPort + ") " +
844
"as this configuration requires JDK 1.5 or later. If running JDK 1.4, can use proxy by setting system properties.");
848
// since know it is a proxy being used, see if have proxy auth
849
String proxyAuth = getProxyAuth(metadata);
850
if (proxyAuth != null)
852
httpURLConn.setRequestProperty("Proxy-Authorization", proxyAuth);
857
externalURL = new URL(url);
858
httpURLConn = (HttpURLConnection) externalURL.openConnection();
860
// Check if proxy is being configured by system properties.
861
if (getSystemProperty("http.proxyHost") != null)
863
String proxyAuth = getProxyAuth(metadata);
864
if (proxyAuth != null)
866
httpURLConn.setRequestProperty("Proxy-Authorization", proxyAuth);
874
private String getProxyAuth(Map metadata)
876
String authString = null;
877
String username = null;
878
String password = null;
880
if (metadata != null)
882
username = (String) metadata.get("http.proxy.username");
884
if (username == null || username.length() == 0)
886
username = getSystemProperty("http.proxy.username");
888
if (metadata != null)
890
password = (String) metadata.get("http.proxy.password");
892
if (password == null)
894
password = getSystemProperty("http.proxy.password");
897
if (username != null && password != null)
899
StringBuffer buffer = new StringBuffer();
900
buffer.append(username);
902
buffer.append(password);
904
String encoded = Base64.encodeBytes(buffer.toString().getBytes());
906
authString = "Basic " + encoded;
913
private String getBasicAuth(Map metadata)
915
String authString = null;
916
String username = null;
917
String password = null;
919
if (metadata != null)
921
username = (String) metadata.get("http.basic.username");
923
if (username == null || username.length() == 0)
925
username = getSystemProperty("http.basic.username");
927
if (metadata != null)
929
password = (String) metadata.get("http.basic.password");
931
if (password == null)
933
password = getSystemProperty("http.basic.password");
936
if (username != null && password != null)
938
StringBuffer buffer = new StringBuffer();
939
buffer.append(username);
941
buffer.append(password);
943
String encoded = Base64.encodeBytes(buffer.toString().getBytes(), Base64.DONT_BREAK_LINES);
945
authString = "Basic " + encoded;
954
* subclasses must implement this method to provide a hook to connect to the remote server, if this applies
955
* to the specific transport. However, in some transport implementations, this may not make must difference since
956
* the connection is not persistent among invocations, such as SOAP. In these cases, the method should
957
* silently return without any processing.
959
* @throws org.jboss.remoting.ConnectionFailedException
962
protected void handleConnect() throws ConnectionFailedException
964
if (InvokerLocator.MULTIHOME.equals(locator.getHost()))
966
Home home = getUsableAddress();
969
throw new ConnectionFailedException(this + " unable to find a usable address for: " + home);
972
String protocol = locator.getProtocol();
973
String path = locator.getPath();
974
Map params = locator.getParameters();
975
locator = new InvokerLocator(protocol, home.host, home.port, path, params);
976
if (log.isDebugEnabled()) log.debug(this + " will use InvokerLocator " + locator);
981
* subclasses must implement this method to provide a hook to disconnect from the remote server, if this applies
982
* to the specific transport. However, in some transport implementations, this may not make must difference since
983
* the connection is not persistent among invocations, such as SOAP. In these cases, the method should
984
* silently return without any processing.
986
protected void handleDisconnect()
988
// NO OP as not statefull connection
992
* Each implementation of the remote client invoker should have
993
* a default data type that is uses in the case it is not specified
994
* in the invoker locator uri.
998
protected String getDefaultDataType()
1000
return HTTPMarshaller.DATATYPE;
1005
* Sets the thread pool to be used for simulating timeouts with jdk 1.4.
1007
public void setTimeoutThreadPool(ThreadPool pool)
1009
this.timeoutThreadPool = pool;
1012
protected void configureParameters()
1014
Object val = configuration.get(HTTPMetadataConstants.NO_THROW_ON_ERROR);
1019
noThrowOnError = Boolean.valueOf((String)val).booleanValue();
1020
log.debug(this + " setting noThrowOnError to " + noThrowOnError);
1024
log.warn(this + " could not convert " +
1025
HTTPMetadataConstants.NO_THROW_ON_ERROR + " value of " +
1026
val + " to a boolean value.");
1030
val = configuration.get(NUMBER_OF_CALL_ATTEMPTS);
1035
numberOfCallAttempts = Integer.valueOf((String)val).intValue();
1036
log.debug(this + " setting numberOfCallRetries to " + numberOfCallAttempts);
1040
log.warn(this + " could not convert " +
1041
NUMBER_OF_CALL_ATTEMPTS + " value of " +
1042
val + " to an int value.");
1046
val = configuration.get(UNMARSHAL_NULL_STREAM);
1051
unmarshalNullStream = Boolean.valueOf((String)val).booleanValue();
1052
log.debug(this + " setting unmarshalNullStream to " + unmarshalNullStream);
1056
log.warn(this + " could not convert " +
1057
UNMARSHAL_NULL_STREAM + " value of " +
1058
val + " to a boolean value.");
1062
val = configuration.get(HTTPMetadataConstants.USE_REMOTING_CONTENT_TYPE);
1067
useRemotingContentType = Boolean.valueOf((String)val).booleanValue();
1068
log.debug(this + " setting useRemotingContent to " + useRemotingContentType);
1072
log.warn(this + " could not convert " +
1073
HTTPMetadataConstants.USE_REMOTING_CONTENT_TYPE + " value of " +
1074
val + " to a boolean value.");
1080
* Gets the thread pool being used for simulating timeouts with jdk 1.4. If one has
1081
* not be specifically set via configuration or call to set it, will always return
1082
* instance of org.jboss.util.threadpool.BasicThreadPool.
1084
public ThreadPool getTimeoutThreadPool()
1086
synchronized (timeoutThreadPoolLock)
1088
if (timeoutThreadPool == null)
1090
int maxNumberThreads = MAX_NUM_TIMEOUT_THREADS_DEFAULT;
1091
int maxTimeoutQueueSize = -1;
1093
BasicThreadPool pool = new BasicThreadPool("HTTP timeout");
1094
log.debug("created new thread pool: " + pool);
1095
Object param = configuration.get(MAX_NUM_TIMEOUT_THREADS);
1096
if (param instanceof String)
1100
maxNumberThreads = Integer.parseInt((String) param);
1102
catch (NumberFormatException e)
1104
log.error("maxNumberThreads parameter has invalid format: " + param);
1107
else if (param != null)
1109
log.error("maxNumberThreads parameter must be a string in integer format: " + param);
1112
param = configuration.get(MAX_TIMEOUT_QUEUE_SIZE);
1114
if (param instanceof String)
1118
maxTimeoutQueueSize = Integer.parseInt((String) param);
1120
catch (NumberFormatException e)
1122
log.error("maxTimeoutQueueSize parameter has invalid format: " + param);
1125
else if (param != null)
1127
log.error("maxTimeoutQueueSize parameter must be a string in integer format: " + param);
1130
pool.setMaximumPoolSize(maxNumberThreads);
1132
if (maxTimeoutQueueSize > 0)
1134
pool.setMaximumQueueSize(maxTimeoutQueueSize);
1136
pool.setBlockingMode(BlockingMode.RUN);
1137
timeoutThreadPool = pool;
1140
return timeoutThreadPool;
1145
* When a WaitingTaskWrapper is run in a BasicThreadPool, the calling thread
1146
* will block for the designated timeout period.
1148
static class WaitingTaskWrapper extends RunnableTaskWrapper
1150
long completeTimeout;
1152
public WaitingTaskWrapper(Runnable runnable, long completeTimeout)
1154
super(runnable, 0, completeTimeout);
1155
this.completeTimeout = completeTimeout;
1157
public int getTaskWaitType()
1159
return Task.WAIT_FOR_COMPLETE;
1161
public String toString()
1163
return "WaitingTaskWrapper[" + completeTimeout + "]";
1167
static private String getSystemProperty(final String name)
1169
if (SecurityUtility.skipAccessControl())
1170
return System.getProperty(name);
1172
String value = null;
1175
value = (String)AccessController.doPrivileged( new PrivilegedExceptionAction()
1177
public Object run() throws Exception
1179
return System.getProperty(name);
1183
catch (PrivilegedActionException e)
1185
throw (RuntimeException) e.getCause();
1191
static private Method getMethod(final Class c, final String name, final Class[] parameterTypes)
1192
throws NoSuchMethodException
1194
if (SecurityUtility.skipAccessControl())
1196
return c.getMethod(name, parameterTypes);
1201
return (Method) AccessController.doPrivileged( new PrivilegedExceptionAction()
1203
public Object run() throws NoSuchMethodException
1205
return c.getMethod(name, parameterTypes);
1209
catch (PrivilegedActionException e)
1211
throw (NoSuchMethodException) e.getCause();
1215
static private void connect(final HttpURLConnection conn) throws IOException
1217
if (SecurityUtility.skipAccessControl())
1225
AccessController.doPrivileged( new PrivilegedExceptionAction()
1227
public Object run() throws IOException
1234
catch (PrivilegedActionException e)
1236
throw (IOException) e.getCause();
1240
static private OutputStream getOutputStream(final HttpURLConnection conn)
1243
if (SecurityUtility.skipAccessControl())
1245
return conn.getOutputStream();
1250
return (OutputStream)AccessController.doPrivileged( new PrivilegedExceptionAction()
1252
public Object run() throws IOException
1254
return conn.getOutputStream();
1258
catch (PrivilegedActionException e)
1260
throw (IOException) e.getCause();
1264
static private int getResponseCode(final HttpURLConnection conn)
1267
if (SecurityUtility.skipAccessControl())
1269
return conn.getResponseCode();
1274
return ((Integer) AccessController.doPrivileged( new PrivilegedExceptionAction()
1276
public Object run() throws IOException
1278
return new Integer(conn.getResponseCode());
1282
catch (PrivilegedActionException e)
1284
throw (IOException) e.getCause();
1288
static private String getResponseMessage(final HttpURLConnection conn)
1291
if (SecurityUtility.skipAccessControl())
1293
return conn.getResponseMessage();
1298
return (String) AccessController.doPrivileged( new PrivilegedExceptionAction()
1300
public Object run() throws IOException
1302
return conn.getResponseMessage();
1306
catch (PrivilegedActionException e)
1308
throw (IOException) e.getCause();
b'\\ No newline at end of file'