%%-------------------------------------------------------------------- %% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1998-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: corba_request.erl %% Description: %% This file contains an corba request server for Orber %% %%----------------------------------------------------------------- -module(corba_request). -behaviour(gen_server). -include_lib("orber/include/corba.hrl"). %%----------------------------------------------------------------- %% External exports %%----------------------------------------------------------------- -export([start/1, stop/0, stop_all/0, create/1, create_schema/1]). %%----------------------------------------------------------------- %% Internal exports %%----------------------------------------------------------------- -export([init/1, terminate/2, install/2, handle_call/3, handle_info/2]). -export([handle_cast/2, dump/0, get_key_from_pid/1]). %%----------------------------------------------------------------- %% Standard interface CORBA::Request %%----------------------------------------------------------------- -export([add_arg/6, invoke/2, delete/1, send/2, get_response/2]). %%----------------------------------------------------------------- %% Mnesia table definition %%----------------------------------------------------------------- -record('corba_request', {reqid, ctx, operation, arg_list, result, req_flags, pid}). %%----------------------------------------------------------------- %% Macros %%----------------------------------------------------------------- -define(dirty_query_context, true). %% This macro returns a read fun suitable for evaluation in a transaction -define(read_function(ReqId), fun() -> mnesia:dirty_read(ReqId) end). %% This macro returns a write fun suitable for evaluation in a transaction -define(write_function(R), fun() -> mnesia:dirty_write(R) end). %% This macro returns a write fun suitable for evaluation in a transaction -define(update(R), fun() -> mnesia:dirty_write(R) end). %% This macro returns a delete fun suitable for evaluation in a transaction -define(delete_function(R), fun() -> mnesia:delete(R) end). -ifdef(dirty_query_context). -define(query_check(Q_res), Q_res). -else. -define(query_check(Q_res), {atomic, Q_res}). -endif. -define(CHECK_EXCEPTION(Res), case Res of {'EXCEPTION', E} -> corba:raise(E); R -> R end). %%----------------------------------------------------------------- %% Debugging function %%----------------------------------------------------------------- dump() -> case catch mnesia:dirty_first('orber_request') of {'EXIT', R} -> io:format("Exited with ~p\n",[R]); Key -> dump_print(Key), dump_loop(Key) end. dump_loop(PreviousKey) -> case catch mnesia:dirty_next('orber_request', PreviousKey) of {'EXIT', R} -> io:format("Exited with ~p\n",[R]); '$end_of_table' -> ok; Key -> dump_print(Key), dump_loop(Key) end. dump_print(Key) -> case catch mnesia:dirty_read({'orber_request', Key}) of {'EXIT', R} -> io:format("Exited with ~p\n",[R]); [X] -> io:format("Req Id: ~p, op: ~p\n",[binary_to_term(X#orber_request.object_key), X#orber_request.pid]); _ -> ok end. %%----------------------------------------------------------------- %% External interface functions %%----------------------------------------------------------------- start(Opts) -> gen_server:start_link({local, orber_requestserver}, orber_request, Opts, []). stop() -> gen_server:call(orber_requestserver, stop, infinity). stop_all() -> Fun = fun() -> mnesia:match_object({orber_request, '_', '_', '_', '_', '_', '_', '_'}) end, case catch mnesia:transaction(Fun) of {atomic, Objects} -> lists:foreach(fun({orber_request, _, _, _, _, _, _, _ }) -> ok %gen_server:call(Pid, stop, infinity) end, Objects); R -> R end. create() -> ?CHECK_EXCEPTION(gen_server:call(orber_requestserver, create, infinity)). create(Ctx, OP, Args, Flags) -> ?CHECK_EXCEPTION(gen_server:call(orber_requestserver, {create, Ctx, OP, Args, Flags}, infinity)). delete(ReqId) -> ?CHECK_EXCEPTION(gen_server:call(orber_requestserver, {delete, ReqId}, infinity)). %%------------------------------------------------------------ %% Implementation of standard interface %%------------------------------------------------------------ add_arg(ReqId, ArgumentName, TC, Value, Len, ArgFlags) -> Request = ets:lookup_element(orber_request, ReqId), case Request of [] -> ok; R -> Args = Request#orber_request.arg_list, NewArgs = lists:append(Args, []), ets:insert(orber_request, NewArgs), ok end. invoke(ReqId, InvokeFlags) -> ok. send(ReqId, InvokeFlags) -> ok. get_response(ReqId, ResponseFlags) -> [{_, Val}] = ets:lookup_element(orber_request, ReqId), Val#'orber_request'.result. %%----------------------------------------------------------------- %% Server functions %%----------------------------------------------------------------- init(Env) -> case mnesia:wait_for_tables(['orber_request'], infinity) of ok -> process_flag(trap_exit, true), {ok, []}; StopReason -> {stop, StopReason} end. terminate(From, Reason) -> ok. install(Timeout, Options) -> %% check if there already exists a database. If not, create one. %% DB_initialized = perhaps_create_schema(Nodelist), %% check if mnesia is running. If not, start mnesia. DB_started = perhaps_start_mnesia(), %% Do we have a complete set of IFR tables? If not, create them. AllTabs = mnesia:system_info(tables), DB_Result = case lists:member(orber_request, AllTabs) of true -> case lists:member({local_content, true}, Options) of true-> mnesia:add_table_copy(orber_request, node(), ram_copies); _ -> mnesia:create_table(orber_request, [{attributes, record_info(fields, orber_objkeys)} |Options]) end; _ -> mnesia:create_table(orber_request, [{attributes, record_info(fields, orber_objkeys)} |Options]) end, Wait = mnesia:wait_for_tables([orber_request], Timeout), %% Check if any error has occured yet. If there are errors, return them. if DB_Result == {atomic, ok}, Wait == ok -> ok; true -> {error, {DB_Result, Wait}} end. %%----------------------------------------------------------------- %% Func: handle_call/3 %% %% Comment: %% In objectkey gen_server all exceptions are tupples and corba:raise %% may not be used. It is too time consuming to add catches in every %% function before returning. On the client side there is a case which %% maps every tupple on the format {'exception', E} to corba:raise(E). %%----------------------------------------------------------------- handle_call(stop, From, State) -> {stop, normal, [], State}; handle_call(create, From, State) -> ReqId = term_to_binary({node(), now()}), _F = ?write_function(#'corba_request'{reqid=ReqId}), R = write_result(mnesia:transaction(_F)), ReqId ?query_check(Qres) = mnesia:dirty_read({orber_request, Objkey}), case Qres of [] -> _F = ?write_function(#orber_requests{object_key=Objkey, pid=Pid}), R = write_result(mnesia:transaction(_F)), if R == ok, pid(Pid) -> link(Pid); true -> true end, {reply, R, State}; X -> {reply, {'EXCEPTION', #'INTERNAL'{completion_status=?COMPLETED_NO}}, State} end; handle_call({delete, ReqId}, From, State) -> ?query_check(Qres) = mnesia:dirty_read({orber_request, ReqId}), case Qres of [] -> true; [X] when pid(X#orber_request.pid) -> unlink(X#orber_request.pid); _ -> true end, _F = ?delete_function({orber_request, ReqId}), R = write_result(mnesia:transaction(_F)), {reply, R, State}. handle_info({'EXIT', Pid, Reason}, State) when pid(Pid) -> _MF = fun() -> mnesia:match_object({orber_request, '_', '_', '_', '_', '_', '_', Pid}) end, ?query_check(Qres) = mnesia:ets(_MF), case Qres of [] -> true; X -> remove_requests(X), unlink(Pid); _ -> true end, {noreply, State}. %%----------------------------------------------------------------- %% Internal Functions %%----------------------------------------------------------------- get_reqids_from_pid(Pid) -> case mnesia:dirty_match_object({orber_request, '_', '_', '_', '_', '_', '_', Pid}) of Keys -> [Keys] _ -> corba:raise(#'OBJECT_NOT_EXIST'{completion_status=?COMPLETED_NO}) end. remove_requests([]) -> ok; remove_requests([H|T]) -> _F = ?delete_function({orber_request, H#orber_request.reqid}), write_result(mnesia:transaction(_F)), remove_requests(T). %%----------------------------------------------------------------- %% Check a read transaction query_result(?query_check(Qres)) -> case Qres of [Hres] -> Hres#orber_request.pid; [] -> {'excpetion', #'OBJECT_NOT_EXIST'{completion_status=?COMPLETED_NO}}; Other -> {'excpetion', #'INTERNAL'{completion_status=?COMPLETED_NO}} end. %%----------------------------------------------------------------- %% Check a write transaction write_result({atomic,ok}) -> ok; write_result(Foo) -> {'excpetion', #'INTERNAL'{completion_status=?COMPLETED_NO}}. create_schema(Nodes) -> case mnesia:system_info(use_dir) of false -> mnesia:create_schema(Nodes); _ -> ok end. perhaps_start_mnesia() -> case mnesia:system_info(is_running) of no -> mnesia:start(); _ -> ok end. %%------------------------------------------------------------ %% Standard gen_server cast handle %% handle_cast(_, State) -> {noreply, State}.