%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
%%-----------------------------------------------------------------
%% File: orber_iiop.erl
%% Description:
%% This file contains the interface to the iiop operations
%%
%%-----------------------------------------------------------------
-module(orber_iiop).
-include_lib("orber/include/corba.hrl").
-include_lib("orber/src/orber_iiop.hrl").
-behaviour(supervisor).
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([start_sup/1, request/8, locate/4]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
-export([init/1, terminate/2, handle_call/3]).
%%-----------------------------------------------------------------
%% Internal defines
%%-----------------------------------------------------------------
-define(DEBUG_LEVEL, 7).
%%-----------------------------------------------------------------
%% External interface functions
%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
%% Func: start_sup/1
%%-----------------------------------------------------------------
start_sup(Opts) ->
supervisor:start_link({local, orber_iiop_sup}, ?MODULE,
{orber_iiop_sup, Opts}).
%%%-----------------------------------------------------------------
%%% Func: connect/1
%%%-----------------------------------------------------------------
%connect(OrbName) ->
% orber_iiop_net:connect(OrbName).
%%%-----------------------------------------------------------------
%%% Func: request/5
%%%-----------------------------------------------------------------
request({Host, Port, InitObjkey, Index, TaggedProfile, HostData},
Op, Parameters, TypeCodes, ResponseExpected, Timeout, IOR, UserCtx) ->
{{Proxy, SysCtx, Interceptors, LocalInterface}, ObjKey, Version} =
connect(Host, Port, InitObjkey, Timeout, [Index], HostData,
TaggedProfile, IOR, UserCtx),
Ctx = add_user_context(SysCtx, UserCtx),
RequestId = orber_request_number:get(),
Env = #giop_env{interceptors = Interceptors, type = out,
flags = orber_env:get_flags(), host = LocalInterface,
version = Version, ctx = Ctx, request_id = RequestId, op = Op,
parameters = Parameters, tc = TypeCodes, objkey = ObjKey,
response_expected = ResponseExpected},
Message = encode_request(Env),
case catch orber_iiop_outproxy:request(Proxy, ResponseExpected, Timeout,
Message, RequestId) of
{'EXCEPTION', MsgExc} ->
corba:raise(MsgExc);
_ when ResponseExpected == false ->
ok;
{reply, ReplyHeader, Rest, Len, ByteOrder, Bytes} ->
case catch decode_reply_body(Interceptors, ObjKey, Op, ReplyHeader,
Version, TypeCodes, Rest, Len, ByteOrder,
Bytes) of
{'EXCEPTION', DecodeException} ->
%% We cannot log this exception since it may be a correct exception.
corba:raise(DecodeException);
{'EXIT', message_error} ->
orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n"
"Got exit(message_error)",
[?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
{'EXIT', Why} ->
orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n"
"Got exit(~p)",
[?LINE, Rest, Version, TypeCodes, Why], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
'message_error' ->
orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p);~n"
"Got message_error",
[?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL),
%% Perhaps a resend should be done when a message error occurs
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
{Result, Par} ->
%% Check request id
case ReplyHeader#reply_header.reply_status of
'no_exception' ->
case Par of
[] ->
Result;
_ ->
list_to_tuple([Result | Par])
end;
'system_exception' ->
corba:raise(Result);
'user_exception' ->
corba:raise(Result);
'location_forward' ->
case get(orber_forward_notify) of
true ->
{location_forward, Result};
_ ->
case catch corba:call(Result, Op, Parameters,
TypeCodes,
[{timeout, Timeout},
{context, UserCtx}]) of
{'EXCEPTION', E} ->
corba:raise(E);
{'EXIT', Reason} ->
orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n"
"location_forward resulted in exit(~p)",
[?LINE, Rest, Version, TypeCodes, Reason], ?DEBUG_LEVEL),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO});
NewResult ->
NewResult
end
end;
'location_forward_perm' ->
%% We should notify the client in this case.
case get(orber_forward_notify) of
true ->
{location_forward, Result};
_ ->
case catch corba:call(Result, Op, Parameters,
TypeCodes,
[{timeout, Timeout},
{context, UserCtx}]) of
{'EXCEPTION', E} ->
corba:raise(E);
{'EXIT', Reason} ->
orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n"
"location_forward_perm resulted in exit(~p)",
[?LINE, Rest, Version, TypeCodes, Reason], ?DEBUG_LEVEL),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO});
NewResult ->
NewResult
end
end;
'needs_addressing_mode' ->
orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n"
"needs_addressing_mode not supported.",
[?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
end
end;
What ->
orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n"
"outproxy-request: ~p", [?LINE, Message, Version, TypeCodes, What], ?DEBUG_LEVEL),
corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
end.
encode_request(#giop_env{interceptors = false} = Env) ->
case catch cdr_encode:enc_request(Env) of
{'EXCEPTION', Exc} ->
orber:dbg("[~p] orber_iiop:request(~p)~n"
"Got exception(~p)",
[?LINE, Env, Exc], ?DEBUG_LEVEL),
corba:raise(Exc);
{'EXIT', R} ->
orber:dbg("[~p] orber_iiop:request:( ~p )~n"
"Got exit(~p)",
[?LINE, Env, R], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
Msg ->
Msg
end;
encode_request(#giop_env{interceptors = {native, Ref, PIs},
objkey = ObjKey, ctx = Ctx, op = Op,
parameters = Params} = Env) ->
Parameters = orber_pi:out_request(PIs, ObjKey, Ctx, Op, Ref, Params),
case catch cdr_encode:enc_request_split(Env) of
{'EXCEPTION', Exc} ->
orber:dbg("[~p] orber_iiop:request( ~p, ~p); exception(~p)",
[?LINE, Env, Parameters, Exc], ?DEBUG_LEVEL),
corba:raise(Exc);
{'EXIT', R} ->
orber:dbg("[~p] orber_iiop:request:( ~p, ~p); got exit(~p)",
[?LINE, Env, Parameters, R], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
{Hdr, Body, HdrLen, _, Flags} ->
NewBody = orber_pi:out_request_enc(PIs, ObjKey, Ctx, Op, Ref, Body),
cdr_encode:enc_giop_message_header(Env, 'request', Flags,
HdrLen+size(NewBody),
[Hdr|NewBody])
end;
encode_request(Env) ->
case catch cdr_encode:enc_request(Env) of
{'EXCEPTION', Exc} ->
orber:dbg("[~p] orber_iiop:request( ~p ); exception(~p)",
[?LINE, Env, Exc], ?DEBUG_LEVEL),
corba:raise(Exc);
{'EXIT', R} ->
orber:dbg("[~p] orber_iiop:request:( ~p ); got exit(~p)",
[?LINE, Env, R], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
Msg ->
Msg
end.
%%-----------------------------------------------------------------
%% Func: locate/1
%%-----------------------------------------------------------------
locate({Host, Port, InitObjkey, Index, TaggedProfile, HostData},
Timeout, IOR, UserCtx) ->
{{Proxy, _Ctx, _Interceptors, LocalInterface}, ObjKey, Version} =
connect(Host, Port, InitObjkey, Timeout, [Index], HostData,
TaggedProfile, IOR, UserCtx),
RequestId = orber_request_number:get(),
Env = #giop_env{version = Version, objkey = ObjKey, request_id = RequestId,
flags = orber_env:get_flags(), host = LocalInterface},
Result =
case catch cdr_encode:enc_locate_request(Env) of
{'EXCEPTION', EncE} ->
orber:dbg("[~p] orber_iiop:locate(~p); exception(~p)",
[?LINE, ObjKey, EncE], ?DEBUG_LEVEL),
corba:raise(EncE);
{'EXIT', EncR} ->
orber:dbg("[~p] orber_iiop:locate(~p); exit(~p)",
[?LINE, ObjKey, EncR], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
Request ->
(catch orber_iiop_outproxy:request(Proxy, true, Timeout,
Request, RequestId))
end,
case Result of
{'EXCEPTION', MsgExc} ->
corba:raise(MsgExc);
{locate_reply, ReplyHeader, Rest, Len, ByteOrder} ->
case catch cdr_decode:dec_locate_reply_body(Version,
ReplyHeader#locate_reply_header.locate_status,
Rest, Len, ByteOrder) of
{'EXCEPTION', DecodeException} ->
orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exception(~p)",
[?LINE, Rest, Version, DecodeException], ?DEBUG_LEVEL),
corba:raise(DecodeException);
{'EXIT', message_error} ->
orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exit(message_error)",
[?LINE, Rest, Version], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
{'EXIT', R} ->
orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exit(~p)",
[?LINE, Rest, Version, R], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
[] ->
ReplyHeader#locate_reply_header.locate_status;
ObjRef ->
{ReplyHeader#locate_reply_header.locate_status, ObjRef}
end;
Other ->
orber:dbg("[~p] orber_iiop:locate(~p); exit(~p)",
[?LINE, ObjKey, Other], ?DEBUG_LEVEL),
corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO})
end.
%%%-----------------------------------------------------------------
%%% Func: cancel/1
%%%-----------------------------------------------------------------
%cancel(X) ->
% ok.
%%%-----------------------------------------------------------------
%%% Func: message_error/1
%%%-----------------------------------------------------------------
%message_error(X) ->
% ok.
%%-----------------------------------------------------------------
%% Server functions
%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
%% Func: init/1
%%-----------------------------------------------------------------
init({orber_iiop_sup, Opts}) ->
IIOP_port = orber:iiop_port(),
SSL_port = orber:iiop_ssl_port(),
SupFlags = {one_for_one, 5, 1000}, %Max 5 restarts in 1 second
PortList = if
SSL_port > -1 ->
[{port, ssl, SSL_port}];
true ->
[]
end,
ChildSpec =
case orber:is_lightweight() of
true ->
[
{orber_iiop_outsup, {orber_iiop_outsup, start,
[sup, Opts]},
permanent, 10000, supervisor, [orber_iiop_outsup]},
{orber_iiop_pm, {orber_iiop_pm, start,
[Opts]},
permanent, 10000, worker, [orber_iiop_pm]}
];
false ->
[{orber_iiop_outsup, {orber_iiop_outsup, start,
[sup, Opts]},
permanent, 10000, supervisor, [orber_iiop_outsup]},
{orber_iiop_pm, {orber_iiop_pm, start,
[Opts]},
permanent, 10000, worker, [orber_iiop_pm]},
{orber_iiop_insup, {orber_iiop_insup, start,
[sup, Opts]},
permanent, 10000, supervisor, [orber_iiop_insup]},
{orber_iiop_socketsup, {orber_iiop_socketsup, start,
[sup, Opts]},
permanent, 10000, supervisor, [orber_iiop_socketsup]},
{orber_iiop_net, {orber_iiop_net, start,
[[{port, normal, IIOP_port} | PortList]]},
permanent, 10000, worker, [orber_iiop_net]}]
end,
{ok, {SupFlags, ChildSpec}}.
%%-----------------------------------------------------------------
%% Func: terminate/2
%%-----------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%-----------------------------------------------------------------
%% Func: handle_call/3
%%-----------------------------------------------------------------
handle_call(_Req, _From, State) ->
{reply, ok, State}.
%%-----------------------------------------------------------------
%% Internal functions
%%-----------------------------------------------------------------
add_user_context([], UserCtx) -> UserCtx;
add_user_context(SysCtx, []) -> SysCtx;
add_user_context(SysCtx, UserCtx) -> SysCtx ++ UserCtx.
decode_reply_body(false, _ObjKey, _Op, ReplyHeader, Version, TypeCodes,
Rest, Len, ByteOrder, Bytes) ->
case ReplyHeader#reply_header.reply_status of
'no_exception' ->
{R, P, _} = cdr_decode:dec_reply_body(Version, TypeCodes, Rest, Len, ByteOrder, Bytes),
{R, P};
'system_exception' ->
{R, _} = cdr_decode:dec_system_exception(Version, Rest, Len, ByteOrder),
{R, []};
'user_exception' ->
{R, _} = cdr_decode:dec_user_exception(Version, Rest, Len, ByteOrder),
{R, []};
'location_forward' ->
{R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]},
Rest, Len, ByteOrder, Bytes),
{R, []};
'location_forward_perm' ->
{R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]},
Rest, Len, ByteOrder, Bytes),
{R, []};
'needs_addressing_mode' ->
{R, _, _} = cdr_decode:dec_reply_body(Version, {'tk_short', [],[]},
Rest, Len, ByteOrder, Bytes),
{R, []}
end;
decode_reply_body(Interceptors, ObjKey, Op, ReplyHeader, Version, TypeCodes,
RestIn, Len, ByteOrder, Bytes) ->
Rest =
case Interceptors of
{portable, _PIs} ->
RestIn;
{native, Ref, PIs} ->
orber_pi:in_reply_enc(PIs, ObjKey,
ReplyHeader#reply_header.service_context,
Op, Ref, RestIn)
end,
Reply =
case ReplyHeader#reply_header.reply_status of
'no_exception' ->
{R, P, _} = cdr_decode:dec_reply_body(Version, TypeCodes, Rest, Len, ByteOrder, Bytes),
{R, P};
'system_exception' ->
{R, _} = cdr_decode:dec_system_exception(Version, Rest, Len, ByteOrder),
{R, []};
'user_exception' ->
{R, _} = cdr_decode:dec_user_exception(Version, Rest, Len, ByteOrder),
{R, []};
'location_forward' ->
{R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]},
Rest, Len, ByteOrder, Bytes),
{R, []};
'location_forward_perm' ->
{R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]},
Rest, Len, ByteOrder, Bytes),
{R, []};
'needs_addressing_mode' ->
{R, _, _} = cdr_decode:dec_reply_body(Version, {'tk_short', [],[]},
Rest, Len, ByteOrder, Bytes),
{R, []}
end,
case Interceptors of
{portable, _PI} ->
Reply;
{native, Refs, PI} ->
orber_pi:in_reply(PI, ObjKey,
ReplyHeader#reply_header.service_context,
Op, Refs, Reply)
end.
%% "Plain" TCP/IP.
connect(Host, Port, Objkey, Timeout, Index,
#host_data{protocol = normal, csiv2_mech = undefined} = HostData,
TaggedProfile, IOR, Ctx) ->
connect2([{Host, Port}], Objkey, Timeout, Index, HostData,
TaggedProfile, IOR, Ctx);
%% "Plain" SSL
connect(Host, _, Objkey, Timeout, Index,
#host_data{protocol = ssl,
ssl_data = #'SSLIOP_SSL'{port = Port},
csiv2_mech = undefined} = HostData,
TaggedProfile, IOR, Ctx) ->
connect2([{Host, Port}], Objkey, Timeout, Index, HostData,
TaggedProfile, IOR, Ctx);
%% TEMPORARY FIX TO AVOID RUNNING CSIv2.
connect(Host, _, Objkey, Timeout, Index,
#host_data{protocol = ssl,
ssl_data = #'SSLIOP_SSL'{port = Port}} = HostData,
TaggedProfile, IOR, Ctx) ->
connect2([{Host, Port}], Objkey, Timeout, Index, HostData,
TaggedProfile, IOR, Ctx);
%% CSIv2 over SSL (TAG_TLS_SEC_TRANS) using the SAS protocol. Note port must equal 0.
connect(_Host, 0, Objkey, Timeout, Index,
#host_data{protocol = ssl,
csiv2_mech =
#'CSIIOP_CompoundSecMech'{target_requires = _TR} = _Mech,
csiv2_addresses = Addresses} = HostData,
TaggedProfile, IOR, Ctx) ->
NewCtx = [#'IOP_ServiceContext'
{context_id=?IOP_SecurityAttributeService,
context_data = #'CSI_SASContextBody'
{label = ?CSI_MsgType_MTEstablishContext,
value = #'CSI_EstablishContext'
{client_context_id = 0, %% Always 0 when stateless.
authorization_token =
[#'CSI_AuthorizationElement'{the_element = []}],
identity_token =
#'CSI_IdentityToken'{label = ?CSI_IdentityTokenType_ITTAbsent,
value = true},
client_authentication_token = []}}}|Ctx],
connect2(Addresses, Objkey, Timeout, Index, HostData,
TaggedProfile, IOR, NewCtx);
%% CSIv2 over SSL (TAG_NULL_TAG) using the SAS protocol.
connect(Host, _, Objkey, Timeout, Index,
#host_data{protocol = ssl,
ssl_data = #'SSLIOP_SSL'{port = Port},
csiv2_mech = Mech} = HostData,
TaggedProfile, IOR, Ctx) when is_record(Mech, 'CSIIOP_CompoundSecMech') ->
connect2([{Host, Port}], Objkey, Timeout, Index, HostData,
TaggedProfile, IOR, Ctx);
%% CSIv2 over TCP (TAG_NULL_TAG) using the SAS protocol.
connect(Host, Port, Objkey, Timeout, Index,
#host_data{protocol = normal,
csiv2_mech = Mech} = HostData,
TaggedProfile, IOR, Ctx) when is_record(Mech, 'CSIIOP_CompoundSecMech') ->
connect2([{Host, Port}], Objkey, Timeout, Index, HostData,
TaggedProfile, IOR, Ctx);
connect(_Host, _Port, _Objkey, _Timeout, _Index, HostData, _TaggedProfile,
IOR, _Ctx) ->
orber:dbg("[~p] orber_iiop:connect(~p)~n"
"Unable to use the supplied IOR.~n"
"Connection Data: ~p", [?LINE, IOR, HostData], ?DEBUG_LEVEL),
corba:raise(#'INV_OBJREF'{completion_status=?COMPLETED_NO}).
connect2(HostPort, Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx) ->
case try_connect(HostPort, HostData#host_data.protocol, Timeout, HostData, Ctx) of
error ->
Alts = iop_ior:get_alt_addr(TaggedProfile),
case try_connect(Alts, HostData#host_data.protocol, Timeout, HostData, Ctx) of
error ->
case iop_ior:get_key(IOR, Index) of
undefined ->
corba:raise(#'COMM_FAILURE'{completion_status = ?COMPLETED_NO});
{'external', {NewHost, NewPort, NewObjkey, NewIndex,
NewTaggedProfile, NewHostData}} ->
connect(NewHost, NewPort, NewObjkey, Timeout, [NewIndex|Index],
NewHostData, NewTaggedProfile, IOR, Ctx);
_What ->
orber:dbg("[~p] orber_iiop:connect2(~p)~n"
"Illegal IOR; contains a mixture of local and external profiles.",
[?LINE, IOR], ?DEBUG_LEVEL),
corba:raise(#'INV_OBJREF'{completion_status=?COMPLETED_NO})
end;
X ->
{X, Objkey, HostData#host_data.version}
end;
X ->
{X, Objkey, HostData#host_data.version}
end.
try_connect([], _, _, _, _) ->
error;
try_connect([{Host, Port}|T], SocketType, Timeout, HostData, Ctx) ->
case catch orber_iiop_pm:connect(Host, Port, SocketType, Timeout,
HostData#host_data.charset,
HostData#host_data.wcharset, Ctx) of
{ok, P, Ctx2, Int, Interface} ->
{P, Ctx2, Int, Interface};
{'EXCEPTION', #'BAD_CONTEXT'{} = CtxExc} ->
orber:dbg("[~p] orber_iiop:try_connect(~p, ~p) failed~n",
[?LINE, Host, Port], ?DEBUG_LEVEL),
corba:raise(CtxExc);
{'EXCEPTION', _PMExc} ->
try_connect(T, SocketType, Timeout, HostData, Ctx);
{'EXIT',{timeout,_}} ->
orber:dbg("[~p] orber_iiop:try_connect(~p, ~p, ~p)~n"
"Connect attempt timed out",
[?LINE, Host, Port, Timeout], ?DEBUG_LEVEL),
try_connect(T, SocketType, Timeout, HostData, Ctx);
{'EXIT', What} ->
orber:dbg("[~p] orber_iiop:try_connect(~p, ~p, ~p)~n"
"Connect attempt resulted in: ~p",
[?LINE, Host, Port, Timeout, What], ?DEBUG_LEVEL),
try_connect(T, SocketType, Timeout, HostData, Ctx)
end.