/**
*
* Copyright 2004-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.geronimo.interop.rmi.iiop.client;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.Socket;
import org.apache.geronimo.interop.SystemException;
import org.apache.geronimo.interop.properties.BooleanProperty;
import org.apache.geronimo.interop.properties.IntProperty;
import org.apache.geronimo.interop.properties.PropertyMap;
import org.apache.geronimo.interop.properties.SystemProperties;
import org.apache.geronimo.interop.rmi.iiop.BadMagicException;
import org.apache.geronimo.interop.rmi.iiop.CdrInputStream;
import org.apache.geronimo.interop.rmi.iiop.CdrOutputStream;
import org.apache.geronimo.interop.rmi.iiop.GiopMessage;
import org.apache.geronimo.interop.rmi.iiop.ObjectRef;
import org.apache.geronimo.interop.rmi.iiop.SecurityInfo;
import org.apache.geronimo.interop.rmi.iiop.SimpleObjectInputStream;
import org.apache.geronimo.interop.rmi.iiop.UnsupportedProtocolVersionException;
import org.apache.geronimo.interop.util.ExceptionUtil;
import org.apache.geronimo.interop.util.InstancePool;
import org.apache.geronimo.interop.util.StringUtil;
import org.apache.geronimo.interop.util.ThreadContext;
import org.omg.GIOP.MsgType_1_1;
import org.omg.GIOP.ReplyHeader_1_2;
import org.omg.GIOP.ReplyStatusType_1_2;
import org.omg.GIOP.RequestHeader_1_2;
import org.omg.GIOP.SystemExceptionReplyBody;
import org.omg.GIOP.SystemExceptionReplyBodyHelper;
import org.omg.GIOP.TargetAddress;
import org.omg.IOP.ServiceContext;
public class Connection
{
private static final byte reservedBA[] = new byte[] { 0, 0, 0};
private int requestid_ = 0;
// http tunnelling related
private boolean httpTunnelled;
private String httpHeaders;
private String webProxyHost;
private int webProxyPort;
public Connection()
{
}
public static Connection getInstance(String endpoint, ObjectRef objectRef, PropertyMap connProps)
{
Connection conn = new Connection();
conn.init(endpoint, objectRef, connProps);
return conn;
}
public static final BooleanProperty simpleIDLProperty =
new BooleanProperty(SystemProperties.class, "org.apache.geronimo.interop.simpleIDL");
public static final IntProperty socketTimeoutProperty =
new IntProperty(Connection.class, "socketTimeout")
.defaultValue(600); // 10 minutes
private static final boolean SIMPLE_IDL = simpleIDLProperty.getBoolean();
private static final ServiceContext[] EMPTY_SERVICE_CONTEXT = {};
private static final byte[] CODE_SET_ENCAPSULATION =
{
(byte)0, // big endian
(byte)0, (byte)0, (byte)0, // padding
(byte)0x05, (byte)0x01, (byte)0x00, (byte)0x01, // 0x05010001 = CodeSet ID for UTF-8
(byte)0x00, (byte)0x01, (byte)0x01, (byte)0x09, // 0x00010109 = CodeSet ID for UTF-16
};
private static final ServiceContext CODE_SET_SERVICE_CONTEXT = new ServiceContext(1, CODE_SET_ENCAPSULATION);
private String url;
private boolean ok;
private InstancePool pool;
private Socket socket;
protected org.apache.geronimo.interop.rmi.iiop.ObjectInputStream input;
protected org.apache.geronimo.interop.rmi.iiop.ObjectOutputStream output;
private CdrOutputStream parameters;
private CdrOutputStream requestOut;
private CdrInputStream results;
private String exceptionType;
private Exception exception;
private RequestHeader_1_2 requestHeader;
private int callForget;
protected java.io.InputStream socketIn;
protected java.io.OutputStream socketOut;
public String getInstanceName()
{
return url;
}
public void close()
{
parameters = null;
input = null;
output = null;
if (ok)
{
pool.put(this);
}
else
{
shutdown();
}
}
public void beforeInvoke() {
ok = false;
parameters = CdrOutputStream.getInstance();
}
public void forget(Object requestKey) {
}
public void invoke(ObjectRef object, String method, Object requestKey, int retryCount)
{
if(object.$getForwardingAddress() != null)
{
object = object.$getForwardingAddress();
}
RequestHeader_1_2 request = requestHeader;
request.request_id = requestid_++;
request.response_flags = 3;
request.target = new TargetAddress();
request.target.object_key(object.$getObjectKey());
request.operation = method;
request.service_context = getServiceContext(object, requestKey, retryCount);
request.reserved = reservedBA; // Sun's generated org.omg.GIOP.RequestHeader_1_2Helper wants this....
if (requestOut == null)
{
requestOut = CdrOutputStream.getInstance();
}
requestOut.write_request(request, parameters);
try
{
if(httpTunnelled)
{
requestOut.send_http_request(socketOut, url, httpHeaders);
}
else
{
requestOut.send_message(socketOut, url);
}
} catch (RuntimeException ex) {
throw ex;
}
requestOut.reset();
if (results == null)
{
results = CdrInputStream.getInstance();
}
else
{
results.reset();
}
results.setNamingContext(object.$getNamingContext());
GiopMessage message;
try
{
if(httpTunnelled)
{
message = results.receive_http_response(socketIn, url);
}
else
{
message = results.receive_message(socketIn, url);//_serverHost);
}
}
catch (BadMagicException ex)
{
throw new SystemException(ex);
}
catch (UnsupportedProtocolVersionException ex)
{
throw new SystemException(ex);
}
catch (RuntimeException ex)
{
throw new RetryInvokeException(ex);
}
switch (message.type)
{
case MsgType_1_1._Reply:
processReply(message.reply, object);
break;
default:
throw new SystemException("TODO: message type = " + message.type);
}
ok = true;
}
public InstancePool getInstancePool() {
return pool;
}
public void setInstancePool(InstancePool pool) {
this.pool = pool;
}
public org.apache.geronimo.interop.rmi.iiop.ObjectInputStream getInputStream() {
if (SIMPLE_IDL) {
return getSimpleInputStream();
}
if (input == null) {
input = org.apache.geronimo.interop.rmi.iiop.ObjectInputStream.getInstance(results);
}
return input;
}
public org.apache.geronimo.interop.rmi.iiop.ObjectOutputStream getOutputStream() {
if (SIMPLE_IDL) {
return getSimpleOutputStream();
}
if (output == null) {
output = org.apache.geronimo.interop.rmi.iiop.ObjectOutputStream.getInstance(parameters);
}
return output;
}
public org.apache.geronimo.interop.rmi.iiop.ObjectInputStream getSimpleInputStream() {
if (input == null) {
input = org.apache.geronimo.interop.rmi.iiop.SimpleObjectInputStream.getInstance(results);
}
return input;
}
public org.apache.geronimo.interop.rmi.iiop.ObjectOutputStream getSimpleOutputStream() {
if (output == null) {
output = org.apache.geronimo.interop.rmi.iiop.SimpleObjectOutputStream.getInstance(parameters);
}
return output;
}
public String getExceptionType() {
return exceptionType;
}
public Exception getException() {
if (exception == null) {
if (exceptionType != null) {
return new SystemException(exceptionType, new org.omg.CORBA.UNKNOWN());
} else {
throw new IllegalStateException("no exception");
}
} else {
return exception;
}
}
public void clearException()
{
exceptionType = null;
exception = null;
}
// TODO: check why we have 'objectRef' parameter???
protected void init(String endpoint, ObjectRef objectRef, PropertyMap connProps)
{
setHttpTunnelledPropsIfTrue(connProps);
if(httpTunnelled)
{
httpInit(endpoint, connProps);
return;
}
url = "iiop://" + endpoint;
UrlInfo urlInfo = UrlInfo.getInstance(url);
String host = urlInfo.getHost();
int port = urlInfo.getPort();
int socketTimeout = socketTimeoutProperty.getInt(endpoint, connProps);
try
{
socket = new Socket(host, port);
socketIn = socket.getInputStream();
socketOut = socket.getOutputStream();
socket.setSoTimeout(1000 * socketTimeout);
}
catch (Exception ex)
{
throw new SystemException(ex);
}
requestHeader = new RequestHeader_1_2();
requestHeader.reserved = reservedBA;
}
private void httpInit(String endpoint, PropertyMap connProps)
{
String host = null;
int port;
url = "iiop://" + endpoint;
int socketTimeout = socketTimeoutProperty.getInt(endpoint, connProps);
if(webProxyHost != null)
{
host = webProxyHost;
port = webProxyPort;
}
else
{
UrlInfo urlInfo = UrlInfo.getInstance(url);
host = urlInfo.getHost();
port = urlInfo.getPort();
}
try
{
socket = new Socket(host, port);
socketIn = socket.getInputStream();
socketOut = socket.getOutputStream();
socket.setSoTimeout(1000 * socketTimeout);
}
catch (IOException ex)
{
throw new SystemException(ex);
}
requestHeader = new RequestHeader_1_2();
requestHeader.reserved = reservedBA;
}
public ServiceContext[] getServiceContext(ObjectRef object, Object requestKey, int retryCount) {
String username;
String password;
SecurityInfo securityInfo = SecurityInfo.getCurrent();
if (securityInfo == null) {
ClientNamingContext namingContext = object.$getNamingContext();
if (namingContext != null) {
username = namingContext.getUsername();
password = namingContext.getPassword();
} else {
username = null;
password = null;
}
} else {
username = securityInfo.username;
password = securityInfo.password;
}
if (username != null && username.length() == 0) {
username = null; // Save network bandwidth in service context.
}
if (password != null && password.length() == 0) {
password = null; // Save network bandwidth in service context.
}
int count = 0;
if (username != null) {
count++;
}
if (password != null) {
count++;
}
if (requestKey != null) {
count++;
}
ServiceContext[] context = new ServiceContext[count];
int index = 0;
context[index++] = CODE_SET_SERVICE_CONTEXT;
if (username != null) {
context[index++] = new ServiceContext(SecurityInfo.TAG_USERNAME, SecurityInfo.encode(username));
}
if (password != null) {
context[index++] = new ServiceContext(SecurityInfo.TAG_PASSWORD, SecurityInfo.encode(password));
}
return context;
}
protected void processReply(ReplyHeader_1_2 reply, ObjectRef object)
{
processReplyServiceContext(reply);
int status = reply.reply_status.value();
switch (status)
{
case ReplyStatusType_1_2._NO_EXCEPTION:
processNormalReply(reply);
break;
case ReplyStatusType_1_2._USER_EXCEPTION:
processUserException(reply);
break;
case ReplyStatusType_1_2._SYSTEM_EXCEPTION:
processSystemException(reply);
break;
case ReplyStatusType_1_2._LOCATION_FORWARD:
processLocationForward(reply, object);
break;
case ReplyStatusType_1_2._LOCATION_FORWARD_PERM:
processLocationForward(reply, object);
break;
case ReplyStatusType_1_2._NEEDS_ADDRESSING_MODE:
throw new SystemException("TODO");
default:
throw new SystemException("reply status = " + status);
}
}
protected void processLocationForward(ReplyHeader_1_2 reply, ObjectRef object)
{
ObjectRef ref = (ObjectRef)results.read_Object();
object.$setForwardingAddress(ref);
throw new RetryInvokeException(new RuntimeException("LOCATION_FORWARD"));
}
protected void processReplyServiceContext(ReplyHeader_1_2 reply) {
ServiceContext[] list = reply.service_context;
int n = list.length;
for (int i = 0; i < n; i++) {
ServiceContext sc = list[i];
if (sc.context_id == 0xCFCFCFCF
|| sc.context_id == 0xDFDFDFDF) {
// "CF..." indicates "Call Forget Request"
// "DF..." indicates "Call Forget Response"
callForget = sc.context_id;
}
}
}
protected void processNormalReply(ReplyHeader_1_2 reply) {
// Intentionally empty.
}
protected void processUserException(ReplyHeader_1_2 reply)
{
exception = null;
String type = results.read_string();
type = StringUtil.removePrefix(type, "IDL:");
type = StringUtil.removeSuffix(type, ":1.0");
if (! (input instanceof SimpleObjectInputStream))
{
if (type.endsWith("Ex"))
{
type = StringUtil.removeSuffix(type, "Ex") + "Exception";
}
}
type = type.replace('/', '.');
exceptionType = type;
ok = true;
}
protected void processSystemException(ReplyHeader_1_2 reply)
{
exceptionType = "???";
SystemExceptionReplyBody replyBody = SystemExceptionReplyBodyHelper.read(results);
String id = replyBody.exception_id;
id = StringUtil.removePrefix(id, "IDL:CORBA/"); // ancient servers might send this!
id = StringUtil.removePrefix(id, "IDL:omg.org/CORBA/");
id = StringUtil.removeSuffix(id, ":1.0");
String causedBy = null;
if (results.hasMoreData())
{
// This is non-standard for IIOP, but if the data isn't present,
// we wont try to read it!
causedBy = ExceptionUtil.causedBy(results.read_string());
}
ok = true;
String exceptionClassName = "org.omg.CORBA." + id;
try
{
Class exceptionClass = ThreadContext.loadClass(exceptionClassName);
Constructor constructor = exceptionClass.getConstructor
(
new Class[] { String.class }
);
org.omg.CORBA.SystemException corbaException;
corbaException = (org.omg.CORBA.SystemException)constructor.newInstance
(
new Object[] { causedBy == null ? "" : causedBy }
);
corbaException.minor = replyBody.minor_code_value;
corbaException.completed = org.omg.CORBA.CompletionStatus.from_int(replyBody.completion_status);
exception = corbaException;
}
catch (Exception ex)
{
// Shouldn't happen, but just in case
ex.printStackTrace();
if (causedBy == null)
{
causedBy = replyBody.exception_id;
}
else
{
causedBy = replyBody.exception_id + "\nCaused by: " + causedBy;
}
exception = new org.omg.CORBA.UNKNOWN(causedBy,
replyBody.minor_code_value,
org.omg.CORBA.CompletionStatus.from_int(replyBody.completion_status));
}
}
private void setHttpTunnelledPropsIfTrue(PropertyMap connprops)
{
if(connprops.get("http") != null)
{
httpTunnelled = true;
}
else
{
httpTunnelled = false;
}
if(httpTunnelled)
{
// get http extra headers if present
httpHeaders = connprops.getProperty("HttpExtraHeader");
if(httpHeaders != null && httpHeaders.toLowerCase().indexOf("user-agent") == -1)
{
httpHeaders += "User-Agent: Geronimo/1.0\r\n";
}
if(httpHeaders == null)
{
httpHeaders = "User-Agent: Geronimo/1.0\r\n";
}
//get webproxy host/port if present:
webProxyHost = connprops.getProperty("WebProxyHost");
String port = connprops.getProperty("WebProxyPort");
if(port != null)
{
try
{
webProxyPort = java.lang.Integer.parseInt(port);
}
catch(java.lang.NumberFormatException e)
{
throw new SystemException(org.apache.geronimo.interop.util.ExceptionUtil.causedBy(e));
}
}
if(port == null && webProxyHost != null)
{
webProxyPort = 80; //default
}
}
else
{
webProxyHost = null;
httpHeaders = null;
}
}
public void shutdown() {
if (socketOut != null) {
try {
socketOut.close();
} catch (Exception ignore) {
}
socketOut = null;
}
if (socketIn != null) {
try {
socketIn.close();
} catch (Exception ignore) {
}
socketIn = null;
}
if (socket != null) {
try {
socket.close();
} catch (Exception ignore) {
}
socket = null;
}
}
}