%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2015. All Rights Reserved.
%%
%% 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.
%%
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% File : orber_ifr_utils.erl
%% Purpose : Common function for the Interface Repository
%%----------------------------------------------------------------------
-module(orber_ifr_utils).
-export([
select/2,
index/2,
construct/3,
get_object/1,
set_object/1,
get_field/2,
set_field/3,
write_result/1,
read_result/1,
ifr_transaction_read/1,
ifr_transaction_write/1,
ifr_transaction_read_write/1,
makeref/1,
unique/0,
existence_check/2,
existence_check/3,
create_repository/0,
init_DB/2, init_DB/3
]).
-include_lib("orber/include/corba.hrl").
-include("orber_ifr.hrl").
-include("ifr_objects.hrl").
%%======================================================================
%% Internal stuff
%%----------------------------------------------------------------------
%% Make a record selection.
%%
%% This code *must* be amended whenever a new record is added in the
%% files ifr_objects.hrl or ../include/ifr_types.hrl
select(Record,Field) when is_record(Record,ir_IRObject) ->
select(Record,record_info(fields,ir_IRObject),Field);
select(Record,Field) when is_record(Record,ir_Contained) ->
select(Record,record_info(fields,ir_Contained),Field);
select(Record,Field) when is_record(Record,ir_Container) ->
select(Record,record_info(fields,ir_Container),Field);
select(Record,Field) when is_record(Record,ir_IDLType) ->
select(Record,record_info(fields,ir_IDLType),Field);
select(Record,Field) when is_record(Record,ir_Repository) ->
select(Record,record_info(fields,ir_Repository),Field);
select(Record,Field) when is_record(Record,ir_ModuleDef) ->
select(Record,record_info(fields,ir_ModuleDef),Field);
select(Record,Field) when is_record(Record,ir_ConstantDef) ->
select(Record,record_info(fields,ir_ConstantDef),Field);
select(Record,Field) when is_record(Record,ir_TypedefDef) ->
select(Record,record_info(fields,ir_TypedefDef),Field);
select(Record,Field) when is_record(Record,ir_StructDef) ->
select(Record,record_info(fields,ir_StructDef),Field);
select(Record,Field) when is_record(Record,ir_UnionDef) ->
select(Record,record_info(fields,ir_UnionDef),Field);
select(Record,Field) when is_record(Record,ir_EnumDef) ->
select(Record,record_info(fields,ir_EnumDef),Field);
select(Record,Field) when is_record(Record,ir_AliasDef) ->
select(Record,record_info(fields,ir_AliasDef),Field);
select(Record,Field) when is_record(Record,ir_PrimitiveDef) ->
select(Record,record_info(fields,ir_PrimitiveDef),Field);
select(Record,Field) when is_record(Record,ir_StringDef) ->
select(Record,record_info(fields,ir_StringDef),Field);
select(Record,Field) when is_record(Record,ir_WstringDef) ->
select(Record,record_info(fields,ir_WstringDef),Field);
select(Record,Field) when is_record(Record,ir_SequenceDef) ->
select(Record,record_info(fields,ir_SequenceDef),Field);
select(Record,Field) when is_record(Record,ir_ArrayDef) ->
select(Record,record_info(fields,ir_ArrayDef),Field);
select(Record,Field) when is_record(Record,ir_ExceptionDef) ->
select(Record,record_info(fields,ir_ExceptionDef),Field);
select(Record,Field) when is_record(Record,ir_AttributeDef) ->
select(Record,record_info(fields,ir_AttributeDef),Field);
select(Record,Field) when is_record(Record,ir_OperationDef) ->
select(Record,record_info(fields,ir_OperationDef),Field);
select(Record,Field) when is_record(Record,ir_InterfaceDef) ->
select(Record,record_info(fields,ir_InterfaceDef),Field);
select(Record,Field) when is_record(Record,ir_FixedDef) ->
select(Record,record_info(fields,ir_FixedDef),Field);
select([],_) -> [];
select(Record,Field) ->
orber:dbg("[~p] orber_ifr_utils:select(~p, ~p);~n"
"Unknown Record Type~n", [?LINE, Record,Field], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO}).
-define(ELEMENT_OFFSET, 2).
select(Record,Fields,Field) ->
Index = index(Fields,Field),
element(?ELEMENT_OFFSET + Index, Record).
index(List,Element) ->
index(List,Element,0).
index([H|_T],Element,Index) when H == Element ->
Index;
index([_H|T],Element,Index) ->
index(T,Element,Index+1);
index([],Element,Index) ->
orber:dbg("[~p] orber_ifr_utils:index(~p, ~p);~n"
"Index error.~n", [?LINE, Element, Index], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO}).
%%%----------------------------------------------------------------------
%%% Construct a record.
%%%
%%% This code *must* be amended whenever a new record is added in the
%%% files ifr_objects.hrl or ../include/ifr_types.hrl
construct(Record,Field,Value) when is_record(Record,ir_IRObject) ->
construct(Record,record_info(fields,ir_IRObject),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_Contained) ->
construct(Record,record_info(fields,ir_Contained),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_Container) ->
construct(Record,record_info(fields,ir_Container),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_IDLType) ->
construct(Record,record_info(fields,ir_IDLType),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_Repository) ->
construct(Record,record_info(fields,ir_Repository),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_ModuleDef) ->
construct(Record,record_info(fields,ir_ModuleDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_ConstantDef) ->
construct(Record,record_info(fields,ir_ConstantDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_TypedefDef) ->
construct(Record,record_info(fields,ir_TypedefDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_StructDef) ->
construct(Record,record_info(fields,ir_StructDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_UnionDef) ->
construct(Record,record_info(fields,ir_UnionDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_EnumDef) ->
construct(Record,record_info(fields,ir_EnumDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_AliasDef) ->
construct(Record,record_info(fields,ir_AliasDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_PrimitiveDef) ->
construct(Record,record_info(fields,ir_PrimitiveDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_StringDef) ->
construct(Record,record_info(fields,ir_StringDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_WstringDef) ->
construct(Record,record_info(fields,ir_WstringDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_SequenceDef) ->
construct(Record,record_info(fields,ir_SequenceDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_ArrayDef) ->
construct(Record,record_info(fields,ir_ArrayDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_ExceptionDef) ->
construct(Record,record_info(fields,ir_ExceptionDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_AttributeDef) ->
construct(Record,record_info(fields,ir_AttributeDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_OperationDef) ->
construct(Record,record_info(fields,ir_OperationDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_InterfaceDef) ->
construct(Record,record_info(fields,ir_InterfaceDef),Field,Value);
construct(Record,Field,Value) when is_record(Record,ir_FixedDef) ->
construct(Record,record_info(fields,ir_FixedDef),Field,Value);
construct(Record,Field,Value) ->
orber:dbg("[~p] orber_ifr_utils:construct(~p, ~p, ~p);~n"
"Unknown Record Type~n",
[?LINE, Record,Field,Value], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO}).
construct(Record,Fields,Field,Value) ->
Index = index(Fields,Field),
setelement(?ELEMENT_OFFSET + Index,Record,Value).
%%%----------------------------------------------------------------------
%%% Read an object from the database
get_object(Objref) ->
%%% Use mnesia:dirty_read/1. It is much faster than doing a transaction.
case mnesia:dirty_read(Objref) of
[Res] ->
Res;
[] ->
[];
Other ->
orber:dbg("[~p] orber_ifr_utils:get_object(~p);~n",
[?LINE, Other], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO})
end.
%%% This is the old code, with a transaction. We might have to revert back
%%% to this at some future time...
%% _F = ?read_function(Objref),
%% read_result(ifr_transaction_read(_F)).
%%%----------------------------------------------------------------------
%%% Write an object to the database
set_object(Object) ->
_F = fun() -> mnesia:write(Object) end,
write_result(ifr_transaction_write(_F)).
%%%----------------------------------------------------------------------
%%% Get the value of a field in a record in the DB
get_field(Objref,FieldName) ->
Object = get_object(Objref),
select(Object,FieldName).
%%%----------------------------------------------------------------------
%%% Atomically set the value of a field in a record in the DB
set_field(Objref,FieldName,Value) ->
_F = fun() -> Object = get_object(Objref),
New_object = construct(Object,FieldName,Value),
mnesia:write(New_object)
end,
write_result(ifr_transaction_write(_F)).
%%%----------------------------------------------------------------------
%%% Check a write transaction
write_result({atomic,ok}) -> ok;
write_result(Wres) ->
orber:dbg("[~p] orber_ifr_utils:write_result(~p);~n",
[?LINE, Wres], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO}).
%%%----------------------------------------------------------------------
%%% Extract the data from a read
read_result({atomic,[Qres]}) -> Qres;
read_result({atomic,[]}) -> [];
read_result(Qres) ->
orber:dbg("[~p] orber_ifr_utils:read_result(~p);~n",
[?LINE, Qres], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO}).
%%%----------------------------------------------------------------------
%%% Execute a transaction or a dirty read/write.
%%%
%%% Since nested transctions will upgrade the inner activity to the
%%% same kind as the outer, we cannot use the check the result in the
%%% above simplistic manner. Therefore we will not mix transaction
%%% with async_dirty (or any of the other transaction-like
%%% activities). A rather extensive rewrite of the query extraction
%%% code must be done first.
ifr_transaction_read(Fun) -> % read synchronously
Tr = mnesia:transaction(Fun),
{atomic, _} = Tr,
Tr.
ifr_transaction_write(Fun) -> % write synchronously
Tr = mnesia:transaction(Fun),
{atomic, _} = Tr,
Tr.
ifr_transaction_read_write(Fun) -> % write synchronously
Tr = mnesia:transaction(Fun),
{atomic, _} = Tr,
Tr.
%%%----------------------------------------------------------------------
%%% Make an object reference from an object
makeref(Obj) ->
[ObjType, ObjID | _] = tuple_to_list(Obj),
{ObjType, ObjID}.
%%%----------------------------------------------------------------------
%%% Make a unique tag.
%%%
%%% The call to term_to_binary is made to hide the representation of the
%%% unique tag. I do this because the tuple generated takes a lot of space
%%% when I dump the database. A binary is simply printed as #Bin, which
%%% is much less obtrusive.
unique() -> term_to_binary({node(), {erlang:system_time(),
erlang:unique_integer()}}).
%%%----------------------------------------------------------------------
%%% Check for an existing object with the Id of the object which is
%%% about to be created.
existence_check({ObjType, ObjID}, Id) ->
Rep = case ObjType of
ir_Repository ->
{ObjType, ObjID};
_ ->
orber_ifr_contained:'_get_containing_repository'({ObjType,
ObjID})
end,
case orber_ifr_repository:lookup_id(Rep, Id) of
[] ->
ok;
What ->
orber:dbg("[~p] orber_ifr_utils:existence_check(~p, ~p, ~p);~n"
"Name clash(?): ~p",
[?LINE, ObjType, ObjID, Id, What], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO})
end.
existence_check(Id, Tab, FieldNum) ->
case mnesia:dirty_index_read(Tab, Id, FieldNum) of
[] ->
ok;
What ->
orber:dbg("[~p] orber_ifr_utils:existence_check(~p, ~p, ~p);~n"
"Name clash(?): ~p",
[?LINE, Id, Tab, FieldNum, What], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO})
end.
%%======================================================================
%% Database initialization
init_DB(Timeout, Options) ->
init_DB(Timeout, Options, false).
init_DB(Timeout, Options, LightIFR) ->
Func = case Options of
{localCopy, IFR_storage_type} when LightIFR == true ->
?ifr_light_record_tuple_list_local(IFR_storage_type);
{localCopy, IFR_storage_type} ->
?ifr_record_tuple_list_local(IFR_storage_type);
_ when LightIFR == true ->
?ifr_light_record_tuple_list(Options);
_ ->
?ifr_record_tuple_list(Options)
end,
create_tables(Func),
Wait = wait_for_tables(LightIFR, Timeout),
db_error_check([Wait],"Database table waiting failed.").
wait_for_tables(true, Timeout) ->
mnesia:wait_for_tables(?ifr_light_object_list, Timeout);
wait_for_tables(_, Timeout) ->
mnesia:wait_for_tables(?ifr_object_list, Timeout).
db_error_check(Checkval,_Message) ->
case lists:any(fun(X) -> X/= ok end, Checkval) of
true ->
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO});
false ->
ok
end.
create_tables([{T,F}|Rest]) ->
case F() of
ok ->
create_tables2(Rest);
{aborted,{already_exists,_}} ->
exit({error, "Orber Mnesia Table(s) already exist. Cannot install Orber."});
Reason ->
orber:dbg("[~p] orber_ifr_utils:create_tables(~p);~n"
"Failed to create the Mnesia table.~n"
"Reason: ~p", [?LINE, T, Reason], ?DEBUG_LEVEL),
exit({error, "Unable to create Mnesia Table"})
end.
create_tables2([]) ->
ok;
create_tables2([{T,F}|Rest]) ->
case F() of
ok ->
create_tables2(Rest);
Reason ->
orber:dbg("[~p] orber_ifr_utils:create_tables2(~p);~n"
"Failed to create the Mnesia table.~n"
"Reason: ~p", [?LINE, T, Reason], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO})
end.
%%%----------------------------------------------------------------------
%%% Create an interface repository. This function should only be called
%%% once, after the database has been set up and initialized.
create_repository() ->
case orber:light_ifr() of
true ->
#orber_light_ifr_ref{data = #lightdata{scope = "",
id = ""}};
false ->
_R = fun() ->
Pat = mnesia:table_info(ir_Repository, wild_pattern),
case [X#ir_Repository.ir_Internal_ID ||
X <- mnesia:match_object(Pat)] of
[] ->
PrimitiveDefs = create_primitivedefs(),
New = #ir_Repository{ir_Internal_ID = unique(),
def_kind = dk_Repository,
contents = [],
primitivedefs = PrimitiveDefs},
mnesia:write(New),
{ir_Repository,New#ir_Repository.ir_Internal_ID};
[Rep_ID] ->
{ir_Repository,Rep_ID};
Error ->
mnesia:abort(Error)
end
end,
case mnesia:transaction(_R) of
{atomic, RepRef} ->
RepRef;
{aborted, Error} ->
orber:dbg("[~p] orber_ifr_utils:create_repository() failed;~n"
"Reason: ~p", [?LINE, Error], ?DEBUG_LEVEL),
corba:raise(#'INTF_REPOS'{completion_status=?COMPLETED_NO})
end
end.
create_primitivedefs() ->
lists:map(fun(Pk) ->
orber_ifr_repository:create_primitivedef(Pk, false)
end,
[pk_void,pk_short,pk_long,pk_longlong,pk_ulonglong,pk_ushort,pk_ulong,
pk_float,pk_double,pk_boolean,pk_char,pk_wchar,pk_octet,pk_any,
pk_TypeCode,pk_Principal,pk_string,pk_wstring,pk_objref]).