aboutsummaryrefslogtreecommitdiffstats
path: root/lib/cosTransactions/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cosTransactions/src')
-rw-r--r--lib/cosTransactions/src/CosTransactions.cfg15
-rw-r--r--lib/cosTransactions/src/CosTransactions.idl193
-rw-r--r--lib/cosTransactions/src/CosTransactions_Terminator_impl.erl362
-rw-r--r--lib/cosTransactions/src/CosTransactions_TransactionFactory_impl.erl178
-rw-r--r--lib/cosTransactions/src/ETraP_Common.erl185
-rw-r--r--lib/cosTransactions/src/ETraP_Common.hrl340
-rw-r--r--lib/cosTransactions/src/ETraP_Server_impl.erl1739
-rw-r--r--lib/cosTransactions/src/Makefile175
-rw-r--r--lib/cosTransactions/src/cosTransactions.app.src43
-rw-r--r--lib/cosTransactions/src/cosTransactions.appup.src6
-rw-r--r--lib/cosTransactions/src/cosTransactions.erl115
-rw-r--r--lib/cosTransactions/src/etrap_logmgr.erl200
12 files changed, 3551 insertions, 0 deletions
diff --git a/lib/cosTransactions/src/CosTransactions.cfg b/lib/cosTransactions/src/CosTransactions.cfg
new file mode 100644
index 0000000000..05709d21cf
--- /dev/null
+++ b/lib/cosTransactions/src/CosTransactions.cfg
@@ -0,0 +1,15 @@
+{this, "CosTransactions::Coordinator"}.
+{this, "CosTransactions::RecoveryCoordinator"}.
+{this, "CosTransactions::Terminator"}.
+{this, "CosTransactions::Control"}.
+{this, "CosTransactions::Resource"}.
+{this, "CosTransactions::TransactionFactory"}.
+{{handle_info, "CosTransactions::Terminator"}, true}.
+{{handle_info, "CosTransactions::TransactionFactory"}, true}.
+{this, "ETraP::Server"}.
+{{handle_info, "ETraP::Server"}, true}.
+{{impl, "CosTransactions::Coordinator"}, "ETraP_Server_impl"}.
+{{impl, "CosTransactions::RecoveryCoordinator"}, "ETraP_Server_impl"}.
+{{impl, "CosTransactions::Control"}, "ETraP_Server_impl"}.
+{{impl, "CosTransactions::Resource"}, "ETraP_Server_impl"}.
+{timeout,"CosTransactions::RecoveryCoordinator"}.
diff --git a/lib/cosTransactions/src/CosTransactions.idl b/lib/cosTransactions/src/CosTransactions.idl
new file mode 100644
index 0000000000..11ec5cbf5b
--- /dev/null
+++ b/lib/cosTransactions/src/CosTransactions.idl
@@ -0,0 +1,193 @@
+#ifndef _COSTRANSACTIONS_IDL
+#define _COSTRANSACTIONS_IDL
+
+#pragma prefix "omg.org"
+
+module CosTransactions {
+
+// DATATYPES
+ enum Status {
+ StatusActive,
+ StatusMarkedRollback,
+ StatusPrepared,
+ StatusCommitted,
+ StatusRolledBack,
+ StatusUnknown,
+ StatusNoTransaction,
+ StatusPreparing,
+ StatusCommitting,
+ StatusRollingBack
+ };
+
+ enum Vote {
+ VoteCommit,
+ VoteRollback,
+ VoteReadOnly
+ };
+
+ // Forward references for interfaces defined later in module
+ interface Control;
+ interface Terminator;
+ interface Coordinator;
+ interface Resource;
+ interface RecoveryCoordinator;
+ interface SubtransactionAwareResource;
+ interface TransactionFactory;
+ interface TransactionalObject;
+ // interface Synchronization;
+
+ // Structure definitions
+ struct otid_t {
+ long formatID; /*format identifier. 0 is OSI TP */
+ long bqual_length;
+ sequence <octet> tid;
+ };
+ struct TransIdentity {
+ Coordinator coord;
+ Terminator term;
+ otid_t otid;
+ };
+ struct PropagationContext {
+ unsigned long timeout;
+ TransIdentity current;
+ sequence <TransIdentity> parents;
+ any implementation_specific_data;
+ };
+ // interface Current;
+
+ // Standard exceptions
+ // Defined in orber/include/corba.hrl
+ // exception TransactionRequired {};
+ // exception TransactionRolledBack {};
+ // exception InvalidTransaction {};
+
+ // Heuristic exceptions
+ exception HeuristicRollback {};
+ exception HeuristicCommit {};
+ exception HeuristicMixed {};
+ exception HeuristicHazard {};
+
+ // Exception from Orb operations
+ exception WrongTransaction {};
+
+ // Other transaction-specific exceptions
+ exception SubtransactionsUnavailable {};
+ exception NotSubtransaction {};
+ exception Inactive {};
+ exception NotPrepared {};
+ exception NoTransaction {};
+ exception InvalidControl {};
+ exception Unavailable {};
+ exception SynchronizationUnavailable {};
+
+ interface TransactionFactory {
+ Control create(in unsigned long time_out);
+ Control recreate(in PropagationContext ctx);
+ };
+
+ interface Control {
+ Terminator get_terminator()
+ raises(Unavailable);
+ Coordinator get_coordinator()
+ raises(Unavailable);
+ };
+
+ interface Terminator {
+ void commit(in boolean report_heuristics)
+ raises( HeuristicMixed,
+ HeuristicHazard );
+ void rollback();
+ };
+
+ interface Coordinator {
+
+ Status get_status();
+ Status get_parent_status();
+ Status get_top_level_status();
+
+ boolean is_same_transaction(in Coordinator tc);
+ boolean is_related_transaction(in Coordinator tc);
+ boolean is_ancestor_transaction(in Coordinator tc);
+ boolean is_descendant_transaction(in Coordinator tc);
+ boolean is_top_level_transaction();
+
+ unsigned long hash_transaction();
+ unsigned long hash_top_level_tran();
+
+ RecoveryCoordinator register_resource(in Resource r)
+ raises(Inactive);
+
+// void register_synchronization (in Synchronization sync)
+// raises(Inactive, SynchronizationUnavailable);
+
+ void register_subtran_aware(in SubtransactionAwareResource r)
+ raises(Inactive, NotSubtransaction);
+
+ void rollback_only()
+ raises(Inactive);
+
+ string get_transaction_name();
+ Control create_subtransaction()
+ raises(SubtransactionsUnavailable, Inactive);
+
+ PropagationContext get_txcontext ()
+ raises(Unavailable);
+ };
+
+
+ interface RecoveryCoordinator {
+ Status replay_completion(in Resource r)
+ raises(NotPrepared);
+ };
+
+ interface Resource {
+ Vote prepare()
+ raises(HeuristicMixed,
+ HeuristicHazard);
+ void rollback()
+ raises( HeuristicCommit,
+ HeuristicMixed,
+ HeuristicHazard );
+ void commit()
+ raises( NotPrepared,
+ HeuristicRollback,
+ HeuristicMixed,
+ HeuristicHazard );
+ void commit_one_phase()
+ raises( HeuristicHazard,
+ HeuristicRollback,
+ HeuristicMixed);
+ void forget();
+ };
+
+// interface TransactionalObject {
+// };
+
+// interface Synchronization : TransactionalObject {
+// void before_completion();
+// void after_completion(in CosTransactions::Status status);
+// };
+
+ interface SubtransactionAwareResource : Resource {
+ void commit_subtransaction(in Coordinator parent);
+ void rollback_subtransaction();
+ };
+
+}; // End of CosTransactions Module
+
+module ETraP {
+
+ // interface Server
+ interface Server :
+ CosTransactions::Coordinator, CosTransactions::Resource,
+ CosTransactions::RecoveryCoordinator, CosTransactions::Control {
+ };
+// interface Server :
+// CosTransactions::Coordinator, CosTransactions::Resource,
+// CosTransactions::RecoveryCoordinator, CosTransactions::Control,
+// CosTransactions::Synchronization {
+// };
+
+}; // End of ETraP Module
+
+#endif
diff --git a/lib/cosTransactions/src/CosTransactions_Terminator_impl.erl b/lib/cosTransactions/src/CosTransactions_Terminator_impl.erl
new file mode 100644
index 0000000000..768e63950c
--- /dev/null
+++ b/lib/cosTransactions/src/CosTransactions_Terminator_impl.erl
@@ -0,0 +1,362 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : CosTransactions_Terminator_impl.erl
+%% Purpose : Support operations to commit or roll-back a transaction.
+%%----------------------------------------------------------------------
+
+-module('CosTransactions_Terminator_impl').
+
+%%--------------- INCLUDES -----------------------------------
+-include_lib("orber/include/corba.hrl").
+%% Local
+-include_lib("ETraP_Common.hrl").
+-include_lib("CosTransactions.hrl").
+
+%%--------------- IMPORTS-------------------------------------
+-import(etrap_logmgr, [log_safe/2, get_next/2]).
+
+%%--------------- EXPORTS-------------------------------------
+%%-compile(export_all).
+-export([commit/3, rollback/2]).
+-export([init/1, terminate/2]).
+-export([handle_call/3, handle_cast/2, handle_info/2, code_change/3]).
+
+%%--------------- LOCAL DATA ---------------------------------
+%-record(terminator, {reg_resources, rollback_only, regname, coordinator}).
+
+%%------------------------------------------------------------
+%% function : init, terminate
+%% Arguments:
+%% Returns :
+%% Effect : Functions demanded by the module ic. Used to initiate
+%% and terminate a gen_server.
+%%------------------------------------------------------------
+
+init(State) ->
+ process_flag(trap_exit,true),
+ case catch start_object(State) of
+ {'EXIT', Reason} ->
+ %% Happens when, for example, we encounter an
+ %% error when reading from the log file.
+ {stop, Reason};
+ Other ->
+ Other
+ end.
+
+start_object(State) ->
+ case catch file:read_file_info(?tr_get_terminator(State)) of
+ {error, enoent} ->
+ %% File does not exist. It's the first time. No restart.
+ ?debug_print("Terminator:init(~p)~n", [?tr_get_terminator(State)]),
+ etrap_logmgr:start(?tr_get_terminator(State)),
+ {ok, State, ?tr_get_timeout(State)};
+ {error, Reason} -> % File exist but error occurred.
+ ?tr_error_msg("CosTransactions_Terminator( ~p ) Cannot open log file: ~p~n",
+ [?tr_get_terminator(State), Reason]),
+ {stop, {error, "unable_to_open_log"}};
+ _ -> % File exists, perform restart.
+ etrap_logmgr:start(?tr_get_terminator(State)),
+ ?debug_print("RESTART Terminator:init(~p)~n",
+ [?tr_get_terminator(State)]),
+ do_restart(State, get_next(?tr_get_terminator(State), start), init)
+ end.
+
+
+terminate(Reason, State) ->
+ ?debug_print("STOP ~p ~p~n", [?tr_get_terminator(State), Reason]),
+ case Reason of
+ normal ->
+ %% normal termination. Transaction completed.
+ log_safe(?tr_get_terminator(State), done),
+ etrap_logmgr:stop(?tr_get_terminator(State)),
+ file:delete(?tr_get_terminator(State)),
+ ok;
+ _ ->
+ ok
+ end.
+
+%%------------------------------------------------------------
+%% function : handle_call, handle_cast, handle_info, code_change
+%% Arguments:
+%% Returns :
+%% Effect : Functions demanded by the module ic.
+%%------------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+handle_call(_,_, State) ->
+ {noreply, State}.
+
+
+handle_cast(_, State) ->
+ {noreply, State}.
+
+
+handle_info(Info, State) ->
+ ?debug_print("Terminator:handle_info(~p)~n", [Info]),
+ Pid = self(),
+ case Info of
+ timeout ->
+ ?tr_error_msg("Object( ~p ) timeout. Rolling back.~n",
+ [?tr_get_terminator(State)]),
+ {stop, normal, State};
+ {suicide, Pid} ->
+ {stop, normal, State};
+ _->
+ {noreply, State}
+ end.
+
+%%------------------------------------------------------------
+%% function : commit
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Heuristics - boolean; report heuristic decisions?
+%% Returns : ok - equal to void
+%% Effect :
+%% Exception: HeuristicMixed - Highest priority
+%% HeuristicHazard - Lowest priority
+%%------------------------------------------------------------
+
+commit(_Self, State, _Heuristics) when ?tr_is_retransmit(State) ->
+ ?debug_print("Terminator:commit() recalled.~n", []),
+ {stop, normal, ?tr_get_reportH(State), State};
+commit(Self, State, Heuristics) ->
+ ?debug_print("Terminator:commit() called.~n", []),
+ NewState = ?tr_set_reportH(State, Heuristics),
+ log_safe(?tr_get_terminator(NewState), {init_commit, NewState}),
+ transmit(Self, NewState, Heuristics).
+
+
+transmit(Self, State, Heuristics) ->
+ case catch 'ETraP_Common':try_timeout(?tr_get_alarm(State)) of
+ false ->
+% catch 'ETraP_Server':before_completion(?tr_get_etrap(State)),
+ case catch 'CosTransactions_Resource':prepare(?tr_get_etrap(State)) of
+ 'VoteCommit' ->
+ evaluate_answer(Self, State, Heuristics,
+ 'ETraP_Common':try_timeout(?tr_get_alarm(State)));
+ 'VoteRollback' ->
+ {stop, normal,
+ {'EXCEPTION',
+ #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ State};
+ 'VoteReadOnly' ->
+ {stop, normal, ok, State};
+ {'EXCEPTION', E} when is_record(E,'CosTransactions_HeuristicMixed'),
+ Heuristics==true->
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+ {stop, normal, {'EXCEPTION', E}, State};
+ {'EXCEPTION', E} when is_record(E,'CosTransactions_HeuristicHazard'),
+ Heuristics==true->
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+ {stop, normal, {'EXCEPTION', E}, State};
+ {'EXCEPTION', E} when is_record(E,'CosTransactions_HeuristicMixed') ->
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+ {stop, normal,
+ {'EXCEPTION',#'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ State};
+ {'EXCEPTION', E} when is_record(E,'CosTransactions_HeuristicHazard') ->
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+ {stop, normal,
+ {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ State};
+ Other ->
+ ?tr_error_msg("Coordinator:prepare( ~p ) failed. REASON ~p~n",
+ [?tr_get_etrap(State), Other]),
+ {stop, normal,
+ {'EXCEPTION',
+ #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ State}
+ end;
+ _ ->
+ %% Timeout, rollback.
+ log_safe(?tr_get_terminator(State), rolled_back),
+ catch 'ETraP_Server':rollback(?tr_get_etrap(State)),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ {stop, normal,
+ {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ State}
+ end.
+
+evaluate_answer(Self, State, Heuristics, false) ->
+ evaluate_answer(Self, State, Heuristics, commit);
+evaluate_answer(Self, State, Heuristics, true) ->
+ evaluate_answer(Self, State, Heuristics, rollback);
+evaluate_answer(_Self, State, Heuristics, Vote) ->
+ case catch 'ETraP_Common':send_stubborn('ETraP_Server', Vote,
+ ?tr_get_etrap(State),
+ ?tr_get_maxR(State),
+ ?tr_get_maxW(State)) of
+ ok ->
+ ?eval_debug_fun({_Self, commit_ok1}, State),
+ log_safe(?tr_get_terminator(State), committed),
+ ?eval_debug_fun({_Self, commit_ok2}, State),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusCommitted'),
+ {stop, normal, ok, State};
+ {'EXCEPTION', E} when Heuristics == true andalso
+ is_record(E,'CosTransactions_HeuristicMixed') ->
+ log_safe(?tr_get_terminator(State), {heuristic, State, E}),
+ ?eval_debug_fun({_Self, commit_heuristic1}, State),
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ {stop, normal, {'EXCEPTION', E}, State};
+ {'EXCEPTION', E} when Heuristics == true andalso
+ is_record(E, 'CosTransactions_HeuristicHazard') ->
+ log_safe(?tr_get_terminator(State), {heuristic, State, E}),
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ {stop, normal, {'EXCEPTION', E}, State};
+ {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') ->
+ log_safe(?tr_get_terminator(State), rolled_back),
+ {stop, normal, {'EXCEPTION', ?tr_hazard}, State};
+ {'EXCEPTION', E} when is_record(E, 'TRANSACTION_ROLLEDBACK') ->
+ log_safe(?tr_get_terminator(State), rolled_back),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ {stop, normal, {'EXCEPTION', E}, State};
+ {'EXCEPTION', E} when is_record(E, 'CosTransactions_HeuristicCommit') ->
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ {stop, normal, ok, State};
+ {'EXCEPTION', E} when is_record(E, 'CosTransactions_HeuristicRollback') ->
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusCommitted'),
+ {stop, normal,
+ {'EXCEPTION',
+ #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ State};
+ _Other when Heuristics == true ->
+ log_safe(?tr_get_terminator(State), rolled_back),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ {stop, normal, {'EXCEPTION', ?tr_hazard}, State};
+ _Other ->
+ log_safe(?tr_get_terminator(State), rolled_back),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ {stop, normal, ok, State}
+ end.
+
+%%-----------------------------------------------------------%
+%% function : rollback
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : ok - equal to void
+%% Effect :
+%%------------------------------------------------------------
+
+rollback(_Self, State) ->
+ ?debug_print("Terminator:rollback() called.~n", []),
+ log_safe(?tr_get_terminator(State), rolled_back),
+ catch 'ETraP_Server':rollback(?tr_get_etrap(State)),
+ {stop, normal, ok, State}.
+
+%%-----------------------------------------------------------%
+%% function : do_restart
+%% Arguments: State - server context
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+%% No data in file. Commit never initiated so we rollback (presumed rollback.
+do_restart(State, eof, init) ->
+ log_safe(?tr_get_terminator(State), rolled_back),
+ catch 'ETraP_Server':rollback(?tr_get_etrap(State)),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State), 'StatusRolledBack'),
+ self() ! {suicide, self()},
+ {ok, State};
+
+do_restart(State, {error, Reason}, _) ->
+ ?tr_error_msg("CosTransactions_Terminator (~p) failed. Cannot read log file: ~p~n",
+ [?tr_get_terminator(State), Reason]),
+ {stop, Reason};
+do_restart(State, eof, Phase) ->
+ ?debug_print("Terminator:do_restart(~p)~n", [Phase]),
+ case Phase of
+ committed ->
+ {ok, ?tr_set_reportH(State, ok)};
+ rolled_back ->
+ self() ! {suicide, self()},
+ {ok, State};
+ init_commit ->
+ case catch corba_object:non_existent(?tr_get_etrap(State)) of
+ true ->
+ self() ! {suicide, self()},
+ {ok, State};
+ _->
+ case transmit(false, State, ?tr_get_reportH(State)) of
+ {stop, normal, ok, NewState} ->
+ {ok, NewState};
+ {stop, normal,
+ {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ NewState} ->
+ self() ! {suicide, self()},
+ {ok, NewState};
+ {stop, normal, {'EXCEPTION', Exc}, NewState} ->
+ if
+ ?tr_dont_reportH(State) ->
+ self() ! {suicide, self()},
+ {ok, NewState};
+ true ->
+ {ok, ?tr_set_reportH(NewState, Exc)}
+ end
+ end
+ end;
+ {heuristic, Exc} ->
+ catch 'ETraP_Server':forget(?tr_get_etrap(State)),
+% catch 'ETraP_Server':after_completion(?tr_get_etrap(State),
+% 'StatusRolledBack'),
+ if
+ ?tr_dont_reportH(State) ->
+ self() ! {suicide, self()},
+ {ok, State};
+ true ->
+ {ok, ?tr_set_reportH(State, {'EXCEPTION',Exc})}
+ end
+ end;
+%% All done.
+do_restart(State, {done, _Cursor}, _Phase) ->
+ ?debug_print("Terminator:do_restart(~p)~n", [_Phase]),
+ self() ! {suicide, self()},
+ {ok, State};
+do_restart(State, {rolled_back, Cursor}, _Phase) ->
+ ?debug_print("Terminator:do_restart(~p)~n", [_Phase]),
+ do_restart(State, get_next(?tr_get_terminator(State), Cursor), rolled_back);
+do_restart(State, {committed, Cursor}, _Phase) ->
+ ?debug_print("Terminator:do_restart(~p)~n", [_Phase]),
+ do_restart(State, get_next(?tr_get_terminator(State), Cursor), committed);
+do_restart(State, {{heuristic, SavedState, Exc}, Cursor}, _Phase) ->
+ ?debug_print("Terminator:do_restart(~p)~n", [_Phase]),
+ do_restart(SavedState, get_next(?tr_get_terminator(State), Cursor),
+ {heuristic, Exc});
+do_restart(State, {{init_commit, SavedState}, Cursor}, _) ->
+ ?debug_print("Terminator:do_restart(~p)~n", [init_commit]),
+ do_restart(SavedState, get_next(?tr_get_terminator(State), Cursor), init_commit).
+
+%%--------------- END OF MODULE ------------------------------
diff --git a/lib/cosTransactions/src/CosTransactions_TransactionFactory_impl.erl b/lib/cosTransactions/src/CosTransactions_TransactionFactory_impl.erl
new file mode 100644
index 0000000000..36e37e2d5f
--- /dev/null
+++ b/lib/cosTransactions/src/CosTransactions_TransactionFactory_impl.erl
@@ -0,0 +1,178 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : CosTransactions_TransactionFactory_impl.erl
+%% Purpose : Is provided to allow the transaction originator to begin
+%% a transaction.
+%%----------------------------------------------------------------------
+
+-module('CosTransactions_TransactionFactory_impl').
+
+%%--------------- INCLUDES -----------------------------------
+-include_lib("orber/include/corba.hrl").
+
+%% Local
+-include_lib("ETraP_Common.hrl").
+-include_lib("CosTransactions.hrl").
+%%--------------- IMPORTS-------------------------------------
+-import('ETraP_Common', [get_option/3]).
+
+%%--------------- EXPORTS-------------------------------------
+-export([create/3, recreate/3, init/1, terminate/2]).
+-export([handle_call/3, handle_cast/2, handle_info/2, code_change/3]).
+
+%%--------------- LOCAL DATA ---------------------------------
+-record(factory, {hashMax, subtrOK, typeCheck, maxRetries, comFailWait}).
+
+%%--------------- LOCAL DEFINITIONS --------------------------
+
+%%------------------------------------------------------------
+%% function : init
+%% Arguments:
+%% Returns :
+%% Effect : Functions demanded by the module ic. Used to initiate
+%% a gen_server.
+%%------------------------------------------------------------
+
+init(Options) when is_list(Options) ->
+ ?debug_print("Factory:init(~p)~n", [Options]),
+ process_flag(trap_exit,true),
+ DefaultValues = [{maxRetries, ?tr_max_retries},
+ {comFailWait, ?tr_comm_failure_wait}|?tr_FAC_DEF],
+ Hash = get_option(hash_max, Options, DefaultValues),
+ SubtrOK = get_option(allow_subtr, Options, DefaultValues),
+ TypeCheck = get_option(typecheck, Options, DefaultValues),
+ TTY = get_option(tty, Options, DefaultValues),
+ LogFile = get_option(logfile, Options, DefaultValues),
+ MaxRetries = get_option(maxRetries, Options, DefaultValues),
+ ComFailWait = get_option(comFailWait, Options, DefaultValues),
+ error_logger:tty(TTY),
+ case LogFile of
+ false ->
+ ok;
+ _->
+ error_logger:logfile({open, LogFile})
+ end,
+ {ok, #factory{typeCheck = TypeCheck, hashMax = Hash, subtrOK = SubtrOK,
+ maxRetries = MaxRetries, comFailWait = ComFailWait}};
+
+init(Options) ->
+ ?tr_error_msg("TransactionFactory~nBad argument: ~p~n", [Options]),
+ corba:raise(?tr_badparam).
+
+
+%%------------------------------------------------------------
+%% function : terminate
+%% Arguments:
+%% Returns :
+%% Effect : Function demanded by the module ic. Used to
+%% terminate a gen_server.
+%%------------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ?debug_print("Factory:terminate(~p)~n", [_Reason]),
+ ok.
+
+%%------------------------------------------------------------
+%% function : handle_call, handle_cast, handle_info, code_change
+%% Arguments:
+%% Returns :
+%% Effect : Functions demanded by the module ic.
+%%------------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+handle_call(_,_, State) ->
+ {noreply, State}.
+
+
+handle_cast(_, State) ->
+ {noreply, State}.
+
+
+handle_info({'EXIT',_From,shutdown}, State) ->
+ ?debug_print("Factory:handle_info(~p)~n", [shutdown]),
+ {stop, shutdown, State};
+handle_info(_Info, State) ->
+ ?debug_print("Factory:handle_info(~p)~n", [_Info]),
+ {noreply, State}.
+
+%%------------------------------------------------------------
+%% function : create
+%% Arguments: TimeOut - rollback the transaction after TimeOut
+%% seconds. If 0 no timeout.
+%% Returns : a Control object
+%% Effect : Creates a new top-level transaction. The Control
+%% can be used to manage or control participation
+%% in the new transaction. Used for direct context
+%% management.
+%%------------------------------------------------------------
+
+create(_Self, State, TimeOut) when is_integer(TimeOut) ->
+ %% Generate objectnames.
+ ETraPName = 'ETraP_Common':create_name("root"),
+ TermName = 'ETraP_Common':create_name("term"),
+ EState = ?tr_create_context(ETraPName, TermName,
+ State#factory.typeCheck,
+ State#factory.hashMax,
+ State#factory.subtrOK,
+ State#factory.maxRetries,
+ State#factory.comFailWait),
+
+ case TimeOut of
+ 0 ->
+ ETraP = ?tr_start_child(?SUP_ETRAP(EState)),
+ {reply, ETraP, State};
+ _ ->
+ if
+ TimeOut > 0 ->
+ {MegaSecs, Secs, _Microsecs} = erlang:now(),
+ EState2 = ?tr_set_alarm(EState, MegaSecs*1000000+Secs+TimeOut),
+ EState3 = ?tr_set_timeout(EState2, TimeOut*1000),
+ ETraP = ?tr_start_child(?SUP_ETRAP(EState3)),
+ {reply, ETraP, State};
+ true ->
+ ?tr_error_msg("TransactionFactory:create( Integer >= 0 )~nBad argument. Not an integer.~n", []),
+ corba:raise(?tr_badparam)
+ end
+ end;
+
+create(_Self, _State, _TimeOut) ->
+ ?tr_error_msg("TransactionFactory:create( Integer >= 0 )~nBad argument. Not an integer.~n", []),
+ corba:raise(?tr_badparam).
+
+
+%%------------------------------------------------------------
+%% function : recreate
+%% Arguments: PropagationContext
+%% Returns : a Control object
+%% Effect :
+%%------------------------------------------------------------
+
+recreate(_Self, _State, #'CosTransactions_PropagationContext'{current = _C}) ->
+ corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_YES}).
+%recreate(Self, State, #'CosTransactions_PropagationContext'{current = C}) ->
+% {reply, C#'CosTransactions_TransIdentity'.coord, State}.
+
+
+%%--------------- END OF MODULE ------------------------------
+
diff --git a/lib/cosTransactions/src/ETraP_Common.erl b/lib/cosTransactions/src/ETraP_Common.erl
new file mode 100644
index 0000000000..dd68e9b038
--- /dev/null
+++ b/lib/cosTransactions/src/ETraP_Common.erl
@@ -0,0 +1,185 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : ETraP_Common.erl
+%% Purpose :
+%%----------------------------------------------------------------------
+
+-module('ETraP_Common').
+
+%%--------------- INCLUDES ----------------------------------
+-include_lib("orber/include/corba.hrl").
+-include_lib("orber/include/ifr_types.hrl").
+%% Local
+-include_lib("ETraP_Common.hrl").
+-include_lib("CosTransactions.hrl").
+
+%%--------------- EXPORTS -----------------------------------
+-export([try_timeout/1,
+ get_option/3,
+ create_name/2,
+ create_name/1,
+ is_debug_compiled/0,
+ send_stubborn/5,
+ create_link/3]).
+
+%%--------------- DEFINITIONS OF CONSTANTS ------------------
+%%------------------------------------------------------------
+%% function : create_link
+%% Arguments: Module - which Module to call
+%% Env/ARgList - ordinary oe_create arguments.
+%% Returns :
+%% Exception:
+%% Effect : Necessary since we want the supervisor to be a
+%% 'simple_one_for_one'. Otherwise, using for example,
+%% 'one_for_one', we have to call supervisor:delete_child
+%% to remove the childs startspecification from the
+%% supervisors internal state.
+%%------------------------------------------------------------
+create_link(Module, Env, ArgList) ->
+ Module:oe_create_link(Env, ArgList).
+
+%%------------------------------------------------------------
+%% function : get_option
+%% Arguments:
+%% Returns :
+%% Exception:
+%% Effect :
+%%------------------------------------------------------------
+
+get_option(Key, OptionList, DefaultList) ->
+ case lists:keysearch(Key, 1, OptionList) of
+ {value,{Key,Value}} ->
+ Value;
+ _ ->
+ case lists:keysearch(Key, 1, DefaultList) of
+ {value,{Key,Value}} ->
+ Value;
+ _->
+ {error, "Invalid option"}
+ end
+ end.
+%%------------------------------------------------------------
+%% function : create_name/2
+%% Arguments:
+%% Returns :
+%% Exception:
+%% Effect :
+%%------------------------------------------------------------
+
+create_name(Name,Type) ->
+ {MSec, Sec, USec} = erlang:now(),
+ lists:concat(['oe_',node(),'_',Type,'_',Name,'_',MSec, '_', Sec, '_', USec]).
+
+%%------------------------------------------------------------
+%% function : create_name/1
+%% Arguments:
+%% Returns :
+%% Exception:
+%% Effect :
+%%------------------------------------------------------------
+
+create_name(Type) ->
+ {MSec, Sec, USec} = erlang:now(),
+ lists:concat(['oe_',node(),'_',Type,'_',MSec, '_', Sec, '_', USec]).
+
+%%------------------------------------------------------------
+%% function : try_timeout
+%% Arguments: Id - name of the timeoutSrv server.
+%% Returns : Boolean
+%% Exception:
+%% Effect :
+%%------------------------------------------------------------
+
+try_timeout(TimeoutAt) ->
+ case TimeoutAt of
+ infinity ->
+ false;
+ _->
+ {MegaSecs, Secs, _Microsecs} = erlang:now(),
+ Time = MegaSecs*1000000+Secs,
+ if
+ Time < TimeoutAt ->
+ false;
+ true ->
+ true
+ end
+ end.
+
+%%------------------------------------------------------------
+%% function : send_stubborn
+%% Arguments: M - module
+%% F - function
+%% A - arguments
+%% MaxR - Maximum no retries
+%% Wait - sleep Wait seconds before next try.
+%% Returns : see effect
+%% Exception:
+%% Effect : Retries repeatedly until anything else besides
+%% 'EXIT', 'COMM_FAILURE' or 'OBJECT_NOT_EXIST'
+%%------------------------------------------------------------
+
+send_stubborn(M, F, A, MaxR, Wait) when is_list(A) ->
+ send_stubborn(M, F, A, MaxR, Wait, 0);
+send_stubborn(M, F, A, MaxR, Wait) ->
+ send_stubborn(M, F, [A], MaxR, Wait, 0).
+send_stubborn(M, F, A, MaxR, _Wait, MaxR) ->
+ ?tr_error_msg("~p:~p( ~p ) failed!! Tried ~p times.~n", [M,F,A,MaxR]),
+ corba:raise(#'INTERNAL'{completion_status=?COMPLETED_NO});
+send_stubborn(M, F, A, MaxR, Wait, Times) ->
+ ?debug_print("~p:~p(~p) # of retries: ~p~n", [M,F,A, Times]),
+ case catch apply(M,F,A) of
+ {'EXCEPTION', E} when is_record(E, 'COMM_FAILURE')->
+ NewTimes = Times +1,
+ timer:sleep(Wait),
+ send_stubborn(M, F, A, MaxR, Wait, NewTimes);
+ {'EXCEPTION', E} when is_record(E, 'TRANSIENT')->
+ NewTimes = Times +1,
+ timer:sleep(Wait),
+ send_stubborn(M, F, A, MaxR, Wait, NewTimes);
+ {'EXCEPTION', E} when is_record(E, 'TIMEOUT')->
+ NewTimes = Times +1,
+ timer:sleep(Wait),
+ send_stubborn(M, F, A, MaxR, Wait, NewTimes);
+ {'EXIT', _} ->
+ NewTimes = Times +1,
+ timer:sleep(Wait),
+ send_stubborn(M, F, A, MaxR, Wait, NewTimes);
+ Other ->
+ ?debug_print("~p:~p(~p) Resulted in: ~p~n", [M,F,A, Other]),
+ Other
+ end.
+
+%%------------------------------------------------------------
+%% function : is_debug_compiled
+%% Arguments:
+%% Returns :
+%% Exception:
+%% Effect :
+%%------------------------------------------------------------
+
+-ifdef(debug).
+ is_debug_compiled() -> true.
+-else.
+ is_debug_compiled() -> false.
+-endif.
+
+%%--------------- END OF MODULE ------------------------------
diff --git a/lib/cosTransactions/src/ETraP_Common.hrl b/lib/cosTransactions/src/ETraP_Common.hrl
new file mode 100644
index 0000000000..5082282efb
--- /dev/null
+++ b/lib/cosTransactions/src/ETraP_Common.hrl
@@ -0,0 +1,340 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : ETraP_Common.hrl
+%% Purpose :
+%%---------------------------------------------------------------------
+
+-ifndef(ETRAP_COMMON_HRL).
+-define(ETRAP_COMMON_HRL, true).
+
+%%--------------- INCLUDES ---------------------------------------------
+-include_lib("orber/include/corba.hrl").
+-include_lib("cosTransactions/include/CosTransactions.hrl").
+
+%%-------- CONSTANTS ---------------------------------------------------
+%% Timeouts
+-define(tr_comm_failure_wait,
+ case catch application:get_env(cosTransactions, comFailWait) of
+ {ok, _Time} when is_integer(_Time) ->
+ _Time;
+ _ ->
+ 5000
+ end).
+
+-define(tr_max_retries,
+ case catch application:get_env(cosTransactions, maxRetries) of
+ {ok, _Max} when is_integer(_Max) ->
+ _Max;
+ _ ->
+ 40
+ end).
+
+%% Exceptions
+% Heuristic
+-define(tr_mixed,
+ #'CosTransactions_HeuristicMixed' {}).
+-define(tr_hazard,
+ #'CosTransactions_HeuristicHazard' {}).
+-define(tr_commit,
+ #'CosTransactions_HeuristicCommit' {}).
+-define(tr_rollback,
+ #'CosTransactions_HeuristicRollback' {}).
+%% Standard
+-define(tr_subunavailable,
+ #'CosTransactions_SubtransactionsUnavailable' {}).
+-define(tr_unavailable,
+ #'CosTransactions_Unavailable' {}).
+-define(tr_unprepared,
+ #'CosTransactions_NotPrepared' {}).
+-define(tr_inactive,
+ #'CosTransactions_Inactive' {}).
+-define(tr_nosync,
+ #'CosTransactions_SynchronizationUnavailable' {}).
+-define(tr_badparam,
+ #'BAD_PARAM'{completion_status=?COMPLETED_NO}).
+-define(tr_NotSubtr,
+ #'CosTransactions_NotSubtransaction' {}).
+
+%% TypeID:s
+-define(tr_Terminator,
+ 'CosTransactions_Terminator':typeID()).
+-define(tr_Coordinator,
+ 'CosTransactions_Coordinator':typeID()).
+-define(tr_Control,
+ 'CosTransactions_Control':typeID()).
+-define(tr_RecoveryCoordinator,
+ 'CosTransactions_RecoveryCoordinator':typeID()).
+-define(tr_SubtransactionAwareResource,
+ 'CosTransactions_SubtransactionAwareResource':typeID()).
+-define(tr_Synchronization,
+ 'CosTransactions_Synchronization':typeID()).
+-define(tr_Resource,
+ 'CosTransactions_Resource':typeID()).
+-define(tr_ETraP,
+ 'ETraP_Server':typeID()).
+-define(tr_TransactionalObject,
+ 'CosTransactions_TransactionalObject':typeID()).
+
+
+%%-------- MISC --------------------------------------------------------
+
+-define(tr_error_msg(Txt, Arg),
+error_logger:error_msg("============ CosTransactions ==============~n"
+ Txt
+ "===========================================~n",
+ Arg)).
+
+
+-define(tr_NIL_OBJ_REF, corba:create_nil_objref()).
+
+-define(tr_FAC_DEF, [{hash_max, 1013},
+ {allow_subtr, true},
+ {typecheck, true},
+ {tty, false},
+ {logfile, false}]).
+
+
+%%-------- Supervisor child-specs ------------------------------------
+-define(FACTORY_NAME, oe_cosTransactionsFactory).
+-define(SUPERVISOR_NAME, cosTransactions_sup).
+-define(SUP_FLAG, {simple_one_for_one,50,10}).
+
+-define(SUP_FAC(Env),
+ ['CosTransactions_TransactionFactory',Env,
+ [{sup_child, true}, {regname, {local, ?FACTORY_NAME}}]]).
+
+-define(SUP_ETRAP(Env),
+ ['ETraP_Server', Env,
+ [{sup_child, true}, {persistent, true},
+ {regname, {global, ?tr_get_etrap(Env)}}]]).
+
+-define(SUP_TERMINATOR(Env),
+ ['CosTransactions_Terminator', Env,
+ [{sup_child, true}, {persistent, true},
+ {regname, {global, ?tr_get_etrap(Env)}}]]).
+
+-define(SUP_CHILD,
+ {"oe_child",
+ {'ETraP_Common',create_link, []},
+ transient,100000,worker,
+ ['ETraP_Common',
+ 'ETraP_Server_impl',
+ 'ETraP_Server',
+ 'CosTransactions_Terminator_impl',
+ 'CosTransactions_Terminator',
+ 'CosTransactions_TransactionFactory_impl',
+ 'CosTransactions_TransactionFactory']}).
+
+
+-define(tr_start_child(SPEC),
+ case supervisor:start_child(?SUPERVISOR_NAME, SPEC) of
+ {ok, Pid, Obj} when is_pid(Pid) ->
+ Obj;
+ _Other->
+ corba:raise(?tr_badparam)
+ end).
+
+-define(tr_start_child_pid(SPEC),
+ supervisor:start_child(?SUPERVISOR_NAME, SPEC)).
+
+-define(tr_terminate_child(Child),
+ supervisor:terminate_child(?SUPERVISOR_NAME, Child)).
+
+-define(tr_delete_child(Child),
+ supervisor:delete_child(?SUPERVISOR_NAME, Child)).
+
+%%-------- DATASTRUCTURES ----------------------------------------------
+%% tr_*_*
+-record(context, {terminator, etrap, recCoord, alarm = infinity,
+ timeout = infinity, parents=[], trid, typeCheck,
+ sub_tr_allowed, hashMax, local, rollback=false,
+ reportH, maxRetries, comFailWait}).
+
+
+%%-------- FUNS --------------------------------------------------------
+-define(tr_IS_MEMBER(Obj),
+ fun(X) ->
+ case catch corba_object:is_equivalent(Obj, X) of
+ true ->
+ true;
+ _ ->
+ false
+ end
+ end).
+
+%% Managing conditional debug functions
+-define(is_debug_compiled, 'ETraP_Common':is_debug_compiled()).
+-define(set_debug_context(L, C),
+ etrap_test_lib:set_debug_context(L, C, ?FILE, ?LINE)).
+
+
+-ifdef(debug).
+-define(put_debug_data(Key, Data), erlang:put(Key, Data)).
+-define(get_debug_data(Key), erlang:get(Key)).
+-define(eval_debug_fun(I, E),
+ etrap_test_lib:eval_debug_fun(I, E, ?FILE, ?LINE)).
+-define(activate_debug_fun(I, F, C),
+ etrap_test_lib:activate_debug_fun(I, F, C, ?FILE, ?LINE)).
+-define(deactivate_debug_fun(I),
+ etrap_test_lib:deactivate_debug_fun(I, ?FILE, ?LINE)).
+-define(debug_print(F,A),
+ io:format("[LINE: ~p] "++F,[?LINE]++A)).
+-define(scratch_debug_fun,
+ etrap_test_lib:scratch_debug_fun()).
+-else.
+-define(put_debug_data(Key, Data), ok).
+-define(get_debug_data(Key), ok).
+-define(eval_debug_fun(I, E), ok).
+-define(activate_debug_fun(I, F, C), ok).
+-define(deactivate_debug_fun(I), ok).
+-define(debug_print(F,A), ok).
+-define(scratch_debug_fun, ok).
+-endif.
+
+
+%%-------- CONSTRUCTORS ------------------------------------------------
+
+-define(tr_create_context(ETraP, Terminator, TypeCheck, HM, SubtrOK, MaxRetries,
+ ComFailWait),
+ #context{etrap = ETraP, terminator = Terminator, typeCheck = TypeCheck,
+ hashMax = HM, sub_tr_allowed = SubtrOK, maxRetries = MaxRetries,
+ comFailWait = ComFailWait}).
+
+
+%%-------- MISC --------------------------------------------------------
+-define(tr_notimeout(Context),
+ 'ETraP_Common':try_timeout(Context#context.alarm) == false).
+-define(tr_is_root(Context), Context#context.parents == []).
+-define(tr_dont_reportH(Context), Context#context.reportH == false).
+-define(tr_is_retransmit(Context),
+ Context#context.reportH =/= undefined,
+ Context#context.reportH =/= true,
+ Context#context.reportH =/= false).
+
+%%-------- SELECTORS ---------------------------------------------------
+
+-define(tr_get_reportH(Context),
+ Context#context.reportH).
+
+-define(tr_get_rollback(Context),
+ Context#context.rollback).
+
+-define(tr_get_subTraOK(Context),
+ Context#context.sub_tr_allowed).
+
+-define(tr_get_hashMax(Context),
+ Context#context.hashMax).
+
+-define(tr_get_local(Context),
+ Context#context.local).
+
+-define(tr_get_trid(Context),
+ Context#context.trid).
+
+-define(tr_get_typeCheck(Context),
+ Context#context.typeCheck).
+
+-define(tr_get_recCoord(Context),
+ Context#context.recCoord).
+
+-define(tr_get_alarm(Context),
+ Context#context.alarm).
+
+-define(tr_get_timeout(Context),
+ Context#context.timeout).
+
+-define(tr_get_etrap(Context),
+ Context#context.etrap).
+
+-define(tr_get_terminator(Context),
+ Context#context.terminator).
+
+-define(tr_get_id(Context),
+ Context#context.self).
+
+-define(tr_get_maxW(Context),
+ Context#context.comFailWait).
+
+-define(tr_get_maxR(Context),
+ Context#context.maxRetries).
+
+-define(tr_get_parents(Context),
+ Context#context.parents).
+
+-define(tr_get_parent(Context),
+ lists:nth(1, Context#context.parents)).
+
+%%-------- MODIFIERS ---------------------------------------------------
+
+-define(tr_set_reportH(Context, Bool),
+ Context#context{reportH = Bool}).
+
+-define(tr_set_rollback(Context, Bool),
+ Context#context{rollback = Bool}).
+
+-define(tr_set_subTraOK(Context, Bool),
+ Context#context{sub_tr_allowed = Bool}).
+
+-define(tr_set_hashMax(Context, HM),
+ Context#context{hashMax = HM}).
+
+-define(tr_reset_local(Context),
+ Context#context{local = undefined}).
+
+-define(tr_set_local(Context, Local),
+ Context#context{local = Local}).
+
+-define(tr_set_trid(Context, TRID),
+ Context#context{trid = TRID}).
+
+-define(tr_set_typeCheck(Context, Bool),
+ Context#context{typeCheck = Bool}).
+
+-define(tr_set_id(Context, ID),
+ Context#context{self = ID}).
+
+-define(tr_set_parents(Context, Parents),
+ Context#context{parents = Parents).
+
+-define(tr_add_parent(Context, Parent),
+ Context#context{parents = [Parent] ++ Context#context.parents}).
+
+-define(tr_set_recCoord(Context, R),
+ Context#context{recCoord = R}).
+
+-define(tr_set_alarm(Context, EC),
+ Context#context{alarm = EC}).
+
+-define(tr_set_timeout(Context, T),
+ Context#context{timeout = T}).
+
+-define(tr_set_etrap(Context, ETraP),
+ Context#context{etrap = ETraP}).
+
+-define(tr_set_terminator(Context, T),
+ Context#context{terminator = T}).
+
+-endif.
+
+%%-------------- EOF ---------------------------------------------------
+
+
diff --git a/lib/cosTransactions/src/ETraP_Server_impl.erl b/lib/cosTransactions/src/ETraP_Server_impl.erl
new file mode 100644
index 0000000000..e2c5d88f9d
--- /dev/null
+++ b/lib/cosTransactions/src/ETraP_Server_impl.erl
@@ -0,0 +1,1739 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : ETraP_Server_impl.erl
+%% Purpose :
+%%----------------------------------------------------------------------
+%% GENERAL CODE COMMENTS:
+%% ######################
+%% TypeChecking incoming arguments:
+%% --------------------------------
+%% We allow the user to configure the system so that external calls
+%% (not CosTransactions calls) may be typechecked or not when calling
+%% for example 'replay_completion'. With typecheck the user will get
+%% instant feedback. But since 'is_a' add quiet a lot extra overhead
+%% if the object is located on a remote ORB. Hence, it is up to the
+%% user to decide; speed vs. "safety".
+%%
+%% Log behavior
+%% ------------
+%% Log files are created in the current directory, which is why the
+%% application requires read/write rights for current directory. The
+%% file name looks like:
+%% "oe_nonode@nohost_subc_939_383117_295538" (the last part is now())
+%% It is equal to what the object is started as, i.e., {regname, {global, X}}.
+%%
+%% If the application is unable to read the log it will exit and the
+%% supervisor definitions (found in ETraP_Common.hrl) determines how
+%% many times we will retry. If it's impossible to read the log it's
+%% considered as a disaster, i.e., user intervention is needed.
+%%
+%% If an Object is unreachable when a Coordinator is trying to inform
+%% of the true outcome of the transaction the application will retry N
+%% times with T seconds wait in between. If it's still impossible to
+%% reach the object it's considered as a disaster, i.e., user
+%% intervention is needed.
+%%
+%%----------------------------------------------------------------------
+
+-module('ETraP_Server_impl').
+
+%%--------------- INCLUDES -----------------------------------
+-include_lib("orber/include/corba.hrl").
+
+%% Local
+-include_lib("cosTransactions/src/ETraP_Common.hrl").
+-include_lib("cosTransactions/include/CosTransactions.hrl").
+
+
+%%--------------- IMPORTS-------------------------------------
+-import('ETraP_Common', [try_timeout/1]).
+
+%%--------------- EXPORTS-------------------------------------
+%%--------------- Inherit from CosTransactions::Resource ----
+-export([prepare/2,
+ rollback/2,
+ commit/2,
+ commit_one_phase/2,
+ forget/2]).
+
+%%--------------- Inherit from CosTransactions::Control -----
+-export([get_terminator/2,
+ get_coordinator/2]).
+
+%%----- Inherit from CosTransactions::RecoveryCoordinator ---
+-export([replay_completion/3]).
+
+%%--------------- Inherit from CosTransactions::Coordinator -
+-export([create_subtransaction/2,
+ get_txcontext/2,
+ get_transaction_name/2,
+ get_parent_status/2,
+ get_status/2,
+ get_top_level_status/2,
+ hash_top_level_tran/2,
+ hash_transaction/2,
+ is_ancestor_transaction/3,
+ is_descendant_transaction/3,
+ is_related_transaction/3,
+ is_same_transaction/3,
+ is_top_level_transaction/2,
+ register_resource/3,
+ register_subtran_aware/3,
+ register_synchronization/3,
+ rollback_only/2]).
+
+%%--------- Inherit from CosTransactions::Synchronization ---
+%-export([before_completion/2,
+% after_completion/3]).
+
+
+%%--------------- gen_server specific ------------------------
+-export([init/1, terminate/2]).
+-export([handle_call/3, handle_cast/2, handle_info/2, code_change/3]).
+
+
+
+%%--------------- LOCAL DATA ---------------------------------
+-record(exc,
+ {rollback = false,
+ mixed = false,
+ hazard = false,
+ unprepared = false,
+ commit = false}).
+
+%%--------------- LOCAL DEFINITIONS --------------------------
+
+%%--------------- MISC MACROS --------------------------------
+-define(etr_log(Log, Data), etrap_logmgr:log_safe(Log, Data)).
+-define(etr_read(Log, Cursor), etrap_logmgr:get_next(Log, Cursor)).
+
+-record(coord,
+ {status, %% Status of the transaction.
+ members = [], %% List of registred resources.
+ votedCommit = [], %% List of the ones that voted commit.
+ raisedHeuristic = [], %% The members which raised an Heur. exc.
+ subAw = [], %% Resorces which want to be informed of outcome.
+ sync = [],
+ exc = void,
+ self,
+ etsR}).
+
+%% Selectors
+-define(etr_get_status(L), L#coord.status).
+-define(etr_get_members(L), lists:reverse(L#coord.members)).
+-define(etr_get_vc(L), lists:reverse(L#coord.votedCommit)).
+-define(etr_get_raisedH(L), lists:reverse(L#coord.raisedHeuristic)).
+-define(etr_get_exc(L), L#coord.exc).
+-define(etr_get_subAw(L), lists:reverse(L#coord.subAw)).
+-define(etr_get_sync(L), lists:reverse(L#coord.sync)).
+-define(etr_get_self(L), L#coord.self).
+-define(etr_get_etsR(L), L#coord.etsR).
+-define(etr_get_init(Env), #coord{}).
+-define(etr_get_exc_init(), #exc{}).
+%% Modifiers
+-define(etr_set_status(L, D), L#coord{status = D}).
+-define(etr_set_members(L, D), L#coord{members = D}).
+-define(etr_add_member(L, D), L#coord{members = [D|L#coord.members]}).
+-define(etr_set_vc(L, D), L#coord{votedCommit = D}).
+-define(etr_add_vc(L, D), L#coord{votedCommit = [D|L#coord.votedCommit]}).
+-define(etr_remove_vc(L, D), L#coord{votedCommit =
+ lists:delete(D, ?etr_get_vc(L))}).
+-define(etr_set_raisedH(L, D), L#coord{raisedHeuristic = [D]}).
+-define(etr_add_raisedH(L, D), L#coord{raisedHeuristic =
+ [D|L#coord.raisedHeuristic]}).
+-define(etr_remove_raisedH(L, D), L#coord{raisedHeuristic =
+ lists:delete(D, ?etr_get_raisedH(L))}).
+-define(etr_set_exc(L, D), L#coord{exc = D}).
+-define(etr_set_subAw(L, D), L#coord{subAw = [D]}).
+-define(etr_add_subAw(L, D), L#coord{subAw = [D|L#coord.subAw]}).
+-define(etr_remove_subAw(L, D), L#coord{subAw =
+ lists:delete(D,?etr_get_subAw(L))}).
+-define(etr_set_sync(L, D), L#coord{sync = [D]}).
+-define(etr_add_sync(L, D), L#coord{sync = [D|L#coord.sync]}).
+-define(etr_remove_sync(L, D), L#coord{sync = lists:delete(D,?etr_get_sync(L))}).
+-define(etr_set_self(L, D), L#coord{self = D}).
+-define(etr_set_etsR(L, D), L#coord{etsR = D}).
+
+
+%%------------------------------------------------------------
+%% function : init, terminate
+%% Arguments:
+%% Returns :
+%% Effect : Functions demanded by the module ic.
+%%------------------------------------------------------------
+
+init(Env) ->
+ process_flag(trap_exit,true),
+ case catch start_object(Env) of
+ {'EXIT', Reason} ->
+ %% Happens when, for example, we encounter an
+ %% error when reading from the log file.
+ {stop, Reason};
+ {'EXCEPTION', E} ->
+ self() ! {suicide, self()},
+ corba:raise(E);
+ Other ->
+ Other
+ end.
+
+
+
+terminate(Reason, {Env, _Local}) ->
+ ?debug_print("STOP ~p ~p~n", [?tr_get_etrap(Env), Reason]),
+ case Reason of
+ normal ->
+ %% normal termination. Transaction completed.
+ etrap_logmgr:stop(?tr_get_etrap(Env)),
+ file:delete(?tr_get_etrap(Env)),
+ ok;
+ _ ->
+ ?tr_error_msg("Object(~p) terminated abnormal.~n",[?tr_get_etrap(Env)]),
+ ok
+ end.
+
+
+%%------------------------------------------------------------
+%% function : handle_call, handle_cast, handle_info, code_change
+%% Arguments:
+%% Returns :
+%% Effect : Functions demanded by the gen_server module.
+%%------------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+handle_call(_,_, State) ->
+ {noreply, State}.
+
+handle_cast(_, State) ->
+ {noreply, State}.
+
+
+handle_info(Info, {Env, Local}) ->
+ ?debug_print("ETraP_Server:handle_info(~p)~n", [Info]),
+ Pid = self(),
+ case Info of
+ timeout ->
+ ?tr_error_msg("Object( ~p ) timeout. Rolling back.~n",
+ [?tr_get_etrap(Env)]),
+ {stop, normal, {Env, Local}};
+ {suicide, Pid} ->
+ {stop, normal, {Env, Local}};
+ _->
+ {noreply, {Env, Local}}
+ end.
+
+
+%%--------------- Inherit from CosTransactions::Control -----
+%%-----------------------------------------------------------%
+%% function : get_terminator
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : a Terminator object reference.
+%% Effect : Supports operations for termination of a transaction
+%%------------------------------------------------------------
+
+get_terminator(Self, {Env, Local}) ->
+ %% Only allows the root-coordinator to export the termonator.
+ %% The reason for this is that only the root-coordinator is allowed
+ %% to initiate termination of a transaction. This is however possible
+ %% to change and add restictions elsewhere, i.e. to verify if the
+ %% commit or rollback call is ok.
+ case catch ?tr_get_parents(Env) of
+ [] -> % No parents, it's a root-coordinator.
+ % Create terminators environment.
+ TEnv = ?tr_set_etrap(Env, Self),
+ T = ?tr_start_child(?SUP_TERMINATOR(TEnv)),
+ {reply, T, {Env, Local}, ?tr_get_timeout(TEnv)};
+ _ ->
+ corba:raise(?tr_unavailable)
+ end.
+
+%%-----------------------------------------------------------%
+%% function : get_coordinator
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : a Coordinator object reference. The OMG specification
+%% states that a object reference must be returned.
+%% Effect : Supports operations needed by resources to participate
+%% in the transaction.
+%%------------------------------------------------------------
+
+get_coordinator(Self, State) ->
+ {reply, Self, State}.
+
+%%----- Inherit from CosTransactions::RecoveryCoordinator ---
+%%-----------------------------------------------------------%
+%% function : replay_completion
+%% Arguments:
+%% Returns : Status
+%% Effect : Provides a hint to the Coordinator that the commit
+%% or rollback operations have not been performed on
+%% the resource.
+%%------------------------------------------------------------
+
+replay_completion(_Self, {Env, Local}, Resource) ->
+ type_check(?tr_get_typeCheck(Env), ?tr_Resource,
+ "RecoveryCoordinator:replay_completion", Resource),
+ case ?etr_get_status(Local) of
+ 'StatusActive' ->
+ corba:raise(?tr_unprepared);
+ Status ->
+ case lists:any(?tr_IS_MEMBER(Resource), ?etr_get_members(Local)) of
+ true ->
+ {reply, Status, {Env, Local}};
+ _ ->
+ corba:raise(#'NO_PERMISSION'{completion_status=?COMPLETED_YES})
+ end
+ end.
+
+%%--------------- Inherit from CosTransactions::Resource ----
+%%-----------------------------------------------------------%
+%% function : prepare
+%% Arguments:
+%% Returns : a Vote
+%% Effect : Is invoked to begin the two-phase-commit on the
+%% resource.
+%%------------------------------------------------------------
+
+prepare(_Self, {Env, Local}) ->
+ %% Set status as prepared. No new Resources are allowed to register.
+ NewL = ?etr_set_status(Local, 'StatusPrepared'),
+
+ ?eval_debug_fun({?tr_get_etrap(Env), root_delay}, Env),
+
+ case catch send_prepare(?etr_get_members(NewL),
+ ?tr_get_alarm(Env)) of
+ readOnly ->
+ %% All voted ReadOnly, done. No need to log.
+ {stop, normal, 'VoteReadOnly', {Env, NewL}};
+ %% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, 'VoteReadOnly', {Env, NewL}};
+% _ ->
+% {reply, 'VoteReadOnly', {Env, NewL}}
+% end;
+ {commit, VC} ->
+ %% All voted Commit.
+ NewL2 = ?etr_set_vc(NewL, VC),
+ case catch try_timeout(?tr_get_alarm(Env)) of
+ false ->
+ case ?etr_log(?tr_get_etrap(Env), {pre_vote, commit, NewL2}) of
+ ok ->
+ ?eval_debug_fun({?tr_get_etrap(Env), prepare1}, Env),
+ {reply, 'VoteCommit', {Env, NewL2}};
+ _->
+ %% Cannot log. Better to be safe than sorry; do rollback.
+ %% However, try to log rollback.
+ ?etr_log(?tr_get_etrap(Env),{pre_vote, rollback, NewL2}),
+ send_decision({Env, NewL2}, 'VoteRollback', rollback)
+ end;
+ _->
+ ?etr_log(?tr_get_etrap(Env),
+ {pre_vote, rollback, NewL2}),
+ %% timeout, reply rollback.
+ send_decision({Env, NewL2}, 'VoteRollback', rollback)
+ end;
+ {rollback, VC} ->
+ %% Rollback vote received.
+ %% Send rollback to commit voters.
+ N2 = ?etr_set_vc(NewL, VC),
+ NewL2 = ?etr_set_status(N2,'StatusRolledBack'),
+ ?etr_log(?tr_get_etrap(Env), {pre_vote, rollback, NewL2}),
+ send_decision({Env, NewL2}, 'VoteRollback', rollback);
+ {'EXCEPTION', E, VC, Obj} ->
+ NewL2 = case is_heuristic(E) of
+ true ->
+ N2 = ?etr_set_vc(NewL, VC),
+ N3 = ?etr_set_exc(N2, E),
+ ?etr_set_raisedH(N3, Obj);
+ _->
+ ?etr_set_vc(NewL, VC)
+ end,
+ ?etr_log(?tr_get_etrap(Env),{pre_vote,rollback, NewL2}),
+ ?eval_debug_fun({?tr_get_etrap(Env), prepare2}, Env),
+ send_decision({Env, NewL2}, {'EXCEPTION', E}, rollback);
+ {failed, VC} ->
+ NewL2 = ?etr_set_vc(NewL, VC),
+ ?etr_log(?tr_get_etrap(Env),{pre_vote, rollback, NewL2}),
+ send_decision({Env, NewL2},
+ {'EXCEPTION', ?tr_hazard}, rollback)
+ end.
+
+
+%%-----------------------------------------------------------%
+%% function : rollback
+%% Arguments: Self - the servers own objref.
+%% {Env, Local} - the servers internal state.
+%% Returns : ok
+%% Effect : Rollback the transaction. If its status is
+%% "StatusRolledBack", this is not the first
+%% rollback call to this server. Might occur if
+%% the parent coordinator just recoeverd from a crasch.
+%% Exception: HeuristicCommit, HeuristicMixed, HeuristicHazard
+%%------------------------------------------------------------
+
+rollback(Self, {Env, Local}) ->
+ case ?etr_get_status(Local) of
+ 'StatusRolledBack' ->
+ case ?etr_get_exc(Local) of
+ void ->
+ {stop, normal, ok, {Env, Local}};
+ %% Replace the reply above if allow synchronization
+ %% Rolled back successfullly earlier.
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, ok, {Env, Local}};
+% _ ->
+% {reply, ok, {Env, Local}}
+% end;
+ E ->
+ %% Already rolledback with heuristic decision
+ corba:raise(E)
+ end;
+ 'StatusPrepared' ->
+ NewL = ?etr_set_status(Local, 'StatusRolledBack'),
+ ?eval_debug_fun({?tr_get_etrap(Env), rollback}, Env),
+ ?etr_log(?tr_get_etrap(Env), rollback),
+ ?eval_debug_fun({?tr_get_etrap(Env), rollback2}, Env),
+ send_decision({Env, NewL}, ok, rollback);
+ 'StatusActive' ->
+ NewL = ?etr_set_status(Local, 'StatusRolledBack'),
+ ?etr_log(?tr_get_etrap(Env), {rollback, NewL}),
+ send_info(?etr_get_members(NewL), 'CosTransactions_Resource', rollback),
+ notify_subtrAware(rollback, ?etr_get_subAw(NewL), Self),
+ {stop, normal, ok, {Env, NewL}}
+%% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, ok, {NewEnv, NewL}};
+% _ ->
+% {reply, ok, {NewEnv, NewL}}
+% end;
+ end.
+
+
+%%-----------------------------------------------------------%
+%% function : commit
+%% Arguments: Self - the servers own objref.
+%% {Env, Local} - the servers internal state.
+%% Returns : ok
+%% Effect : Commit the transaction.
+%% Exception: HeuristicRollback, HeuristicMixed, HeuristicHazard,
+%% NotPrepared
+%%------------------------------------------------------------
+
+commit(_Self, {Env, Local}) ->
+ case ?etr_get_status(Local) of
+ 'StatusPrepared' ->
+ ?eval_debug_fun({?tr_get_etrap(Env), commit}, Env),
+ NewL = ?etr_set_status(Local, 'StatusCommitted'),
+ ?etr_log(?tr_get_etrap(Env),commit),
+ ?eval_debug_fun({?tr_get_etrap(Env), commit2}, Env),
+ send_decision({Env, NewL}, ok, commit);
+ 'StatusCommitted' ->
+ case ?etr_get_exc(Local) of
+ void ->
+ {stop, normal, ok, {Env, Local}};
+ %% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, ok, {Env, Local}};
+% _ ->
+% {reply, ok, {Env, Local}}
+% end;
+ E->
+ corba:raise(E)
+ end;
+ _ ->
+ corba:raise(?tr_unprepared)
+ end.
+
+%%-----------------------------------------------------------%
+%% function : commit_one_phase
+%% Arguments: Self - the servers own objref.
+%% {Env, Local} - the servers internal state.
+%% Returns : ok
+%% Effect : Commit the transaction using one-phase commit.
+%% Use ONLY when there is only one registered Resource.
+%% Exception: HeuristicRollback, HeuristicMixed, HeuristicHazard,
+%% TRANSACTION_ROLLEDBACK
+%%------------------------------------------------------------
+
+commit_one_phase(_Self, {Env, Local}) ->
+ case ?etr_get_members(Local) of
+ [Resource] ->
+ case ?etr_get_status(Local) of
+ 'StatusActive' ->
+ %% Set status as prepared. No new Resources are allowed to register.
+ NewL = ?etr_set_status(Local, 'StatusPrepared'),
+ ?eval_debug_fun({?tr_get_etrap(Env), onePC}, Env),
+ case try_timeout(?tr_get_alarm(Env)) of
+ false ->
+ case catch 'CosTransactions_Resource':prepare(Resource) of
+ 'VoteCommit' ->
+ case try_timeout(?tr_get_alarm(Env)) of
+ false ->
+ send_decision({Env, NewL}, ok, commit, [Resource]);
+ _->
+ %% Timeout, rollback.
+ send_decision({Env, NewL},
+ {'EXCEPTION',
+ #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ rollback, [Resource])
+ end;
+ 'VoteRollback' ->
+ {stop, normal,
+ {'EXCEPTION',
+ #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ {Env, NewL}};
+ 'VoteReadOnly' ->
+ {stop, normal, ok, {Env, NewL}};
+ {'EXCEPTION', E}
+ when is_record(E, 'CosTransactions_HeuristicMixed') ->
+ {reply, {'EXCEPTION', E}, {Env, NewL}};
+ {'EXCEPTION', E}
+ when is_record(E, 'CosTransactions_HeuristicHazard') ->
+ {reply, {'EXCEPTION', E}, {Env, NewL}};
+ Other ->
+ ?tr_error_msg("Coordinator:prepare( ~p ) failed. REASON ~p~n",
+ [Resource, Other]),
+ {stop, normal,
+ {'EXCEPTION',
+ #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ {Env, NewL}}
+ end;
+ _->
+ NewL2 = ?etr_set_status(NewL, 'StatusRolledBack'),
+ send_info(Resource, 'CosTransactions_Resource', rollback),
+ {stop, normal,
+ {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ {Env, NewL2}}
+ %% Replace the reply above if allow synchronization
+% case ?etr_get_sync(NewL2) of
+% [] ->
+% send_info(Resource, 'CosTransactions_Resource', rollback),
+% {stop, normal,
+% {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+% {Env, NewL2}};
+% _ ->
+% send_info(Resource, 'CosTransactions_Resource', rollback),
+% {reply,
+% {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+% {Env, NewL2}}
+% end
+ end;
+ _ ->
+ case evaluate_status(?etr_get_status(Local)) of
+ commit ->
+ test_exc(set_exception(?etr_get_exc_init(),
+ ?etr_get_exc(Local)),
+ commit, ok, {Env, Local});
+ _->
+ test_exc(set_exception(?etr_get_exc_init(),
+ ?etr_get_exc(Local)),
+ rollback,
+ {'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
+ {Env, Local})
+ end
+ end;
+ _->
+ {reply, {'EXCEPTION', #'NO_PERMISSION'{completion_status=?COMPLETED_NO}},
+ {Env, Local}}
+ end.
+
+%%-----------------------------------------------------------%
+%% function : forget
+%% Arguments: Self - the servers own objref.
+%% State - the servers internal state.
+%% Returns : ok
+%% Effect : The resource can forget all knowledge about the
+%% transaction. Terminate this server.
+%%------------------------------------------------------------
+
+forget(_Self, {Env, Local}) ->
+ ?etr_log(?tr_get_etrap(Env), forget_phase),
+ send_forget(?etr_get_raisedH(Local), ?tr_get_etrap(Env)),
+ {stop, normal, ok, {Env, ?etr_set_exc(Local, void)}}.
+%% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, ok, {Env, ?etr_set_exc(Local, void)}};
+% _ ->
+% {reply, ok, {Env, ?etr_set_exc(Local, void)}}
+% end.
+
+%%--------------- Inherrit from CosTransactions::Coordinator -
+
+%%-----------------------------------------------------------%
+%% function : get_status
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : Status
+%% Effect : Returns the status of the transaction associated
+%% with the target object.
+%%------------------------------------------------------------
+
+get_status(_Self, {Env, Local}) ->
+ {reply, ?etr_get_status(Local), {Env, Local}}.
+
+
+%%-----------------------------------------------------------%
+%% function : get_parent_status
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : Status
+%% Effect : Returns the status of the parent transaction
+%% associated with the target object. If top-level
+%% transaction equal to get_status.
+%%------------------------------------------------------------
+
+get_parent_status(_Self, {Env, Local}) ->
+ case catch ?tr_get_parents(Env) of
+ [] ->
+ {reply, ?etr_get_status(Local), {Env, Local}};
+ [Parent|_] ->
+ case catch 'CosTransactions_Coordinator':get_status(Parent) of
+ {'EXCEPTION', _E} ->
+ corba:raise(?tr_unavailable);
+ {'EXIT', _} ->
+ corba:raise(?tr_unavailable);
+ Status ->
+ {reply, Status, {Env, Local}}
+ end
+ end.
+
+
+%%-----------------------------------------------------------%
+%% function : get_top_level_status
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : Status
+%% Effect : Returns the status of the top-level transaction
+%% associated with the target object. If top-level
+%% transaction equal to get_status.
+%%------------------------------------------------------------
+
+get_top_level_status(_Self, {Env, Local}) ->
+ case catch ?tr_get_parents(Env) of
+ [] ->
+ {reply, ?etr_get_status(Local), {Env, Local}};
+ Ancestrors ->
+ case catch 'CosTransactions_Coordinator':get_status(lists:last(Ancestrors)) of
+ {'EXCEPTION', _E} ->
+ corba:raise(?tr_unavailable);
+ {'EXIT', _} ->
+ corba:raise(?tr_unavailable);
+ Status ->
+ {reply, Status, {Env, Local}}
+ end
+ end.
+
+
+%%-----------------------------------------------------------%
+%% function : is_same_transaction
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Coordinator object reference
+%% Returns : boolean
+%% Effect :
+%%------------------------------------------------------------
+
+is_same_transaction(Self, {Env, Local}, Coordinator) ->
+ type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
+ "Coordinator:is_same_transaction", Coordinator),
+ {reply, corba_object:is_equivalent(Self, Coordinator), {Env, Local}}.
+
+%%------------------------------------------------------------
+%% function : is_related_transaction
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Coordinator object reference
+%% Returns : boolean
+%% Effect :
+%%------------------------------------------------------------
+
+is_related_transaction(_Self, {_Env, _Local}, _Coordinator) ->
+ corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_YES}).
+% type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
+% "Coordinator:is_related_transaction", Coordinator),
+% {reply, false, {Env, Local}}.
+
+
+%%------------------------------------------------------------
+%% function : is_ancestor_transaction
+%% Coordinator object reference
+%% Returns : boolean
+%% Effect :
+%%------------------------------------------------------------
+
+is_ancestor_transaction(_Self, {_Env, _Local}, _Coordinator) ->
+ corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_YES}).
+% type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
+% "Coordinator:is_ancestor_transaction", Coordinator),
+% {reply, false, {Env, Local}}.
+
+
+%%-----------------------------------------------------------%
+%% function : is_descendant_transaction
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Coordinator object reference
+%% Returns : boolean
+%% Effect :
+%%------------------------------------------------------------
+
+is_descendant_transaction(Self, {Env, Local}, Coordinator) ->
+ type_check(?tr_get_typeCheck(Env), ?tr_Coordinator,
+ "Coordinator:is_descendant_transaction", Coordinator),
+ {reply,
+ lists:any(?tr_IS_MEMBER(Coordinator), [Self|?tr_get_parents(Env)]),
+ {Env, Local}}.
+
+%%-----------------------------------------------------------%
+%% function : is_top_level_transaction
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : boolean
+%% Effect :
+%%------------------------------------------------------------
+
+is_top_level_transaction(_Self, {Env, Local}) ->
+ case catch ?tr_get_parents(Env) of
+ [] ->
+ {reply, true, {Env, Local}};
+ _ ->
+ {reply, false, {Env, Local}}
+ end.
+
+%%-----------------------------------------------------------%
+%% function : hash_transaction
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : hash code
+%% Effect : Returns a hash code for the transaction associated
+%% with the target object.
+%%------------------------------------------------------------
+
+hash_transaction(Self, {Env, Local}) ->
+ {reply, corba_object:hash(Self, ?tr_get_hashMax(Env)), {Env, Local}}.
+
+
+%%-----------------------------------------------------------%
+%% function : hash_top_level_tran
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : hash code
+%% Effect : Returns a hash code for the top-level transaction
+%% associated with the target object. Equals
+%% hash_transaction if it's a top-level transaction.
+%%------------------------------------------------------------
+
+hash_top_level_tran(Self, {Env, Local}) ->
+ case ?tr_get_parents(Env) of
+ [] ->
+ {reply,
+ corba_object:hash(Self, ?tr_get_hashMax(Env)),
+ {Env, Local}};
+ Ancestrors ->
+ case catch corba_object:hash(lists:last(Ancestrors),
+ ?tr_get_hashMax(Env)) of
+ {'EXCEPTION', _E} ->
+ corba:raise(?tr_unavailable);
+ Hash ->
+ {reply, Hash, {Env, Local}}
+ end
+ end.
+
+
+
+%%-----------------------------------------------------------%
+%% function : register_resource
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Resource object reference
+%% Returns : RecoveryCoordinator (can be used during recovery)
+%% Effect : Registers the specified resource as as participant
+%% in the transaction associated with the target object.
+%% Exception: Inactive - Is prepared or terminated.
+%%------------------------------------------------------------
+
+register_resource(Self, {Env, Local}, Resource) ->
+ type_check(?tr_get_typeCheck(Env), ?tr_Resource,
+ "Coordinator:register_resource", Resource),
+ case ?etr_get_status(Local) of
+ 'StatusActive' -> % ok to register the Resource.
+ NewLocal = ?etr_add_member(Local, Resource),
+ RecoveryCoord = corba:create_subobject_key(Self, ?tr_get_etrap(Env)),
+ {reply, RecoveryCoord, {Env, NewLocal}, ?tr_get_timeout(Env)};
+ _-> % Not active anymore. New members not ok.
+ corba:raise(?tr_inactive)
+ end.
+
+
+
+%%-----------------------------------------------------------%
+%% function : register_subtran_aware
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% SubTransactionAwareResource object reference
+%% Returns : -
+%% Effect : Registers the specified object such that it
+%% will be notified when the subtransaction has
+%% commited or rolled back.
+%%------------------------------------------------------------
+
+register_subtran_aware(Self, {Env, Local}, SubTrAwareResource) ->
+ case ?tr_get_parents(Env) of
+ [] ->
+ corba:raise(?tr_NotSubtr);
+ _->
+ type_check(?tr_get_typeCheck(Env), ?tr_SubtransactionAwareResource,
+ "Coordinator:register_subtran_aware", SubTrAwareResource),
+ NewL = ?etr_add_subAw(Local, SubTrAwareResource),
+ {reply, ok, {Env, ?etr_set_self(NewL, Self)},
+ ?tr_get_timeout(Env)}
+ end.
+
+%%-----------------------------------------------------------%
+%% function : register_synchronization
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Synchronization
+%% Returns : -
+%% Effect :
+%%------------------------------------------------------------
+
+register_synchronization(_Self, {_Env, _Local}, _Synchronization) ->
+ corba:raise(#'CosTransactions_SynchronizationUnavailable'{}).
+
+%register_synchronization(Self, {Env, Local}, Synchronization) ->
+% type_check(?tr_get_typeCheck(Env), ?tr_Synchronization,
+% "Coordinator:register_synchronization", Synchronization),
+% case ?etr_get_status(Local) of
+% 'StatusActive' ->
+% case catch ?tr_get_parents(Env) of
+% [] ->
+% {reply, ok, {Env, ?etr_add_sync(Local, Synchronization)},
+% ?tr_get_timeout(Env)};
+% [Parent|_] ->
+% case catch 'ETraP_Server':register_synchronization(Parent, Self) of
+% {'EXCEPTION', E} ->
+% corba:raise(E);
+% ok ->
+% {reply, ok, {Env, ?etr_add_sync(Local, Synchronization)},
+% ?tr_get_timeout(Env)};
+% What ->
+% corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_MAYBE})
+% end
+% end;
+% _ ->
+% corba:raise(?tr_inactive)
+% end.
+
+%%-----------------------------------------------------------%
+%% function : rollback_only
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : -
+%% Effect : The transaction associated with the target object
+%% is modified so that rollback IS the result.
+%%------------------------------------------------------------
+
+rollback_only(Self, {Env, Local}) ->
+ case ?etr_get_status(Local) of
+ 'StatusActive' ->
+ NewL = ?etr_set_status(Local, 'StatusRolledBack'),
+ NewEnv = ?tr_set_rollback(Env, true),
+ ?etr_log(?tr_get_etrap(Env),{rollback, NewL}),
+ send_info(?etr_get_members(NewL), 'CosTransactions_Resource', rollback),
+ notify_subtrAware(rollback, ?etr_get_subAw(NewL), Self),
+ {stop, normal, ok, {NewEnv, NewL}};
+%% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, ok, {NewEnv, NewL}};
+% _ ->
+% {reply, ok, {NewEnv, NewL}}
+% end;
+ _ ->
+ corba:raise(?tr_inactive)
+ end.
+
+%%-----------------------------------------------------------%
+%% function : get_transaction_name
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : string - which describes the transaction associated
+%% with the target object.
+%% Effect : Intended for debugging.
+%%------------------------------------------------------------
+
+get_transaction_name(_Self, {Env, Local}) ->
+ {reply, ?tr_get_etrap(Env), {Env, Local}}.
+
+%%-----------------------------------------------------------%
+%% function : create_subtransaction
+%% Arguments: Self - its own object reference.
+%% State - Gen-Server State
+%% Returns : A control object if subtransactions are allowed,
+%% otherwise an exception is raised.
+%% Effect : A new subtransaction is created whos parent is
+%% the transaction associated with the target object.
+%% Exception: SubtransactionUnavailabe - no support for nested
+%% transactions.
+%% Inactive - already been prepared.
+%%------------------------------------------------------------
+
+create_subtransaction(Self, {Env, Local}) ->
+ case ?etr_get_status(Local) of
+ 'StatusActive' ->
+ case ?tr_get_subTraOK(Env) of
+ true ->
+ ETraPName = 'ETraP_Common':create_name("subc"),
+ Tname = 'ETraP_Common':create_name("subt"),
+
+ %% Create context for the new object.
+ State = ?tr_create_context(ETraPName, Tname,
+ ?tr_get_typeCheck(Env),
+ ?tr_get_hashMax(Env),
+ ?tr_get_subTraOK(Env),
+ ?tr_get_maxR(Env),
+ ?tr_get_maxW(Env)),
+
+
+ State2 = ?tr_add_parent(State, Self),
+
+ State3 = ?tr_set_alarm(State2, ?tr_get_alarm(Env)),
+
+ State4 = ?tr_set_timeout(State3, ?tr_get_timeout(Env)),
+
+ Control = ?tr_start_child(?SUP_ETRAP(State4)),
+ %% Set the SubCoordinator object reference and register it as participant.
+ SubCoord = 'CosTransactions_Control':get_coordinator(Control),
+ NewLocal = ?etr_add_member(Local, SubCoord),
+ {reply, Control, {Env, NewLocal}, ?tr_get_timeout(Env)};
+ _ ->
+ %% subtransactions not allowed, raise exception.
+ corba:raise(?tr_subunavailable)
+ end;
+ _->
+ corba:raise(?tr_inactive)
+ end.
+
+%%-----------------------------------------------------------%
+%% function : get_txcontext
+%% Arguments:
+%% Returns : PropagationContext
+%% Effect :
+%%------------------------------------------------------------
+
+get_txcontext(_Self, {_Env, _Local}) ->
+ corba:raise(#'CosTransactions_Unavailable'{}).
+
+%get_txcontext(Self, {Env, Local}) ->
+% Otid = #'CosTransactions_otid_t'{formatID=0,
+% bqual_length=0,
+% tid=[corba_object:hash(Self,
+% ?tr_get_hashMax(Env))]},
+% TrIDs = create_TransIdentities(?tr_get_parents(Env), Env, [], Otid),
+% C=case ?tr_get_parents(Env) of
+% [] ->
+% #'CosTransactions_TransIdentity'{coord=Self,
+% term=?tr_get_terminator(Env),
+% otid=Otid};
+% _->
+% #'CosTransactions_TransIdentity'{coord=Self,
+% term=?tr_NIL_OBJ_REF,
+% otid=Otid}
+% end,
+% case ?tr_get_timeout(Env) of
+% infinity ->
+% #'CosTransactions_PropagationContext'{timeout=0,
+% current= C,
+% parents=TrIDs};
+% T ->
+% #'CosTransactions_PropagationContext'{timeout=T/1000,
+% current= C,
+% parents=TrIDs}
+% end.
+
+%create_TransIdentities([], _, Parents, _) -> Parents;
+%create_TransIdentities([Phead|Ptail], Env, Parents, Otid) ->
+% NO=Otid#'CosTransactions_TransIdentity'{otid=
+% corba_object:hash(Phead,
+% ?tr_get_hashMax(Env))},
+% create_TransIdentities([Phead|Ptail], Env, Parents++
+% [#'CosTransactions_TransIdentity'{coord=Phead,
+% term=?tr_NIL_OBJ_REF,
+% otid=NO}],
+% Otid).
+
+
+%%--------- Inherit from CosTransactions::Synchronization ---
+
+%%-----------------------------------------------------------%
+%% function : before_completion
+%% Arguments:
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+%before_completion(Self, {Env, Local}) ->
+% send_info(?etr_get_sync(Local),
+% 'CosTransactions_Synchronization', before_completion),
+% {reply, ok, {Env, Local}}.
+
+%%-----------------------------------------------------------%
+%% function : after_completion
+%% Arguments:
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+%after_completion(Self, {Env, Local}, Status) ->
+% send_info(?etr_get_sync(Local), Status,
+% 'CosTransactions_Synchronization', after_completion),
+% {stop, normal, ok, {Env, Local}}.
+
+%%--------------- IMPLEMENTATION SPECIFIC -------------------
+%%-----------------------------------------------------------%
+%% function : start_object
+%% Arguments:
+%% Returns : EXIT, EXCEPTION, or {ok, State}
+%% Effect : used by init/1 only.
+%%------------------------------------------------------------
+
+start_object(Env)->
+ ?put_debug_data(self, Env),
+ Local = ?etr_get_init(Env),
+ LogName = ?tr_get_etrap(Env),
+ case catch file:read_file_info(LogName) of
+ {error, enoent} ->
+ %% File does not exist. It's the first time. No restart.
+ ?debug_print("ETraP_Server:init(~p)~n",[?tr_get_etrap(Env)]),
+ etrap_logmgr:start(LogName),
+ {ok,
+ {Env, ?etr_set_status(Local, 'StatusActive')},
+ ?tr_get_timeout(Env)};
+ {error, Reason} ->
+ %% File exist but error occurred.
+ ?tr_error_msg("Control (~p) Cannot open log file: ~p~n",
+ [LogName, Reason]),
+ {stop, "unable_to_open_log"};
+ _ ->
+ %% File exists, perform restart.
+ etrap_logmgr:start(LogName),
+ ?debug_print("RESTART ~p~n", [?tr_get_etrap(Env)]),
+ prepare_restart({Env, ?etr_set_status(Local, 'StatusUnknown')},
+ ?etr_read(?tr_get_etrap(Env), start))
+ end.
+
+
+%%-----------------------------------------------------------%
+%% function : send_prepare
+%% Arguments: List of registred resources.
+%% Returns : ok - equal to void
+%% Effect : calls send_prepare/3, which sends a prepare call
+%% to resources participating in the transaction and then collect
+%% their votes. send_prepare will block until
+%% it recieves a reply from the resource.
+%%------------------------------------------------------------
+
+send_prepare(RegResources, Alarm) ->
+ send_prepare(RegResources, [], Alarm).
+
+% All voted ReadOnly. We are done.
+send_prepare([], [], _) ->
+ readOnly;
+
+% All voted commit (VC) or ReadOnly.
+send_prepare([], VC, Alarm) ->
+ case catch try_timeout(Alarm) of
+ false ->
+ {commit, VC};
+ _->
+ {rollback, VC}
+ end;
+
+send_prepare([Rhead|Rtail], VC, Alarm) ->
+ ?debug_print("send_prepare()~n",[]),
+ case catch 'CosTransactions_Resource':prepare(Rhead) of
+ 'VoteCommit' ->
+ case catch try_timeout(Alarm) of
+ false ->
+ _Env = ?get_debug_data(self),
+ ?eval_debug_fun({?tr_get_etrap(_Env), send_prepare}, _Env),
+ send_prepare(Rtail, VC++[Rhead], Alarm);
+ _->
+ %% Timeout, rollback. However, the resource did vote
+ %% commit. Add it to the list.
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {rollback, VC++[Rhead]}
+ end;
+ 'VoteRollback' ->
+ %% Don't care about timeout since we voted rollback.
+ %% A rollback received. No need for more prepare-calls.
+ %% See OMG 10-51, Transaction Service:v1.0
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {rollback, VC};
+ 'VoteReadOnly' ->
+ case catch try_timeout(Alarm) of
+ false ->
+ send_prepare(Rtail, VC, Alarm);
+ _->
+ %% timeout, reply rollback.
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {rollback, VC}
+ end;
+ {'EXCEPTION',E} when is_record(E, 'TIMEOUT') ->
+ ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
+ [Rhead]),
+ %% Since we use presumed abort we will rollback the transaction.
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {rollback, VC};
+ {'EXCEPTION',E} when is_record(E, 'TRANSIENT') ->
+ ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
+ [Rhead]),
+ %% Since we use presumed abort we will rollback the transaction.
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {rollback, VC};
+ {'EXCEPTION',E} when is_record(E, 'COMM_FAILURE') ->
+ ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
+ [Rhead]),
+ %% Since we use presumed abort we will rollback the transaction.
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {rollback, VC};
+ {'EXCEPTION', E} when is_record(E, 'OBJECT_NOT_EXIST') ->
+ ?tr_error_msg("Coordinator:prepare( ~p )~nObject unreachable.~n",
+ [Rhead]),
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {rollback, VC};
+ {'EXCEPTION', Exc} ->
+ ?tr_error_msg("Coordinator:prepare( ~p )~nThe Object raised exception: ~p~n",
+ [Rhead, Exc]),
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ %% This can occur if a subtransaction get one or more
+ %% "VoteCommit" followed by a "VoteRollback".
+ %% The subtransaction then do a send_decision(rollback),
+ %% which can generate Heuristic decisions. Must rollback
+ %% since at least one participant voted rollback.
+ {'EXCEPTION', Exc, VC, Rhead};
+ Other ->
+ ?tr_error_msg("Coordinator:prepare( ~p ) failed. REASON ~p~n",
+ [Rhead, Other]),
+ send_info(Rtail, 'CosTransactions_Resource', rollback),
+ {failed, VC}
+ end.
+
+%%-----------------------------------------------------------%
+%% function : type_check
+%% Arguments: Bool - perform typecheck?
+%% ID - Type it should be.
+%% Func - Name of the function (for error_msg)
+%% Obj - objectrefernce to test.
+%% Returns : 'ok' or raises exception.
+%% Effect :
+%%------------------------------------------------------------
+type_check(false, _, _, _) ->
+ ok;
+type_check(_, ID, Func, Obj) ->
+ case catch corba_object:is_a(Obj,ID) of
+ true ->
+ ok;
+ _ ->
+ ?tr_error_msg("~p( ~p ) Bad argument!!~n", [Func, Obj]),
+ corba:raise(?tr_badparam)
+ end.
+
+%%-----------------------------------------------------------%
+%% function : is_heuristic
+%% Arguments: Exception
+%% Returns : boolean
+%% Effect : Returns true if the exception is a heuristic exc.
+%%------------------------------------------------------------
+
+is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicMixed') -> true;
+is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicHazard') -> true;
+is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicCommit') -> true;
+is_heuristic(E) when is_record(E, 'CosTransactions_HeuristicRollback') -> true;
+is_heuristic(_) -> false.
+
+%%-----------------------------------------------------------%
+%% function : exception_set
+%% Arguments: Genserver state
+%% Returns :
+%% Effect : Used when restarting.
+%%------------------------------------------------------------
+
+exception_set({Env,Local}) ->
+ case ?etr_get_exc(Local) of
+ void ->
+ self() ! {suicide, self()},
+ {ok, {Env, Local}};
+ _ ->
+ {ok, {Env, Local}}
+ end.
+
+%%-----------------------------------------------------------%
+%% function : set_exception
+%% Arguments: Locally defined #exc{}
+%% Heuristic mixed or hazard Exeption
+%% Returns : Altered locally defined #exc{}
+%% Effect : Set the correct tuple member to true.
+%%------------------------------------------------------------
+
+set_exception(Exc, E) when is_record(E, 'CosTransactions_HeuristicMixed') ->
+ Exc#exc{mixed = true};
+set_exception(Exc, E) when is_record(E, 'CosTransactions_HeuristicHazard') ->
+ Exc#exc{hazard = true};
+set_exception(Exc, _) -> Exc.
+
+%%-----------------------------------------------------------%
+%% function : send_forget
+%% Arguments:
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+send_forget([], _) -> ok;
+send_forget([Rhead|Rtail], LogName) ->
+ ?debug_print("send_forget()~n",[]),
+ _Env = ?get_debug_data(self),
+ case catch 'CosTransactions_Resource':forget(Rhead) of
+ ok ->
+ ?eval_debug_fun({?tr_get_etrap(_Env), send_forget1}, _Env),
+ ?etr_log(LogName, {forgotten, Rhead}),
+ ?eval_debug_fun({?tr_get_etrap(_Env), send_forget2}, _Env),
+ send_forget(Rtail, LogName);
+ Other ->
+ ?tr_error_msg("CosTransactions_Coordinator failed sending forget to ~p~nREASON: ~p~n",
+ [Rhead, Other]),
+ ?eval_debug_fun({?tr_get_etrap(_Env), send_forget3}, _Env),
+ ?etr_log(LogName, {not_forgotten, Rhead}),
+ ?eval_debug_fun({?tr_get_etrap(_Env), send_forget4}, _Env),
+ send_forget(Rtail, LogName)
+ end.
+
+%%-----------------------------------------------------------%
+%% function : send_decision
+%% Arguments: List of registred resources which vote commit.
+%% Vote - the outcome of the transaction.
+%% Returns : ok - equal to void
+%% Effect : Inform those who voted commit of the outcome.
+%% They who voted rollback already knows the outcome.
+%% They who voted ReadOnly are not affected.
+%%------------------------------------------------------------
+
+%%-- Adding extra parameters
+send_decision({Env, Local}, Reply, Vote) ->
+ send_decision({Env, Local}, Reply, ?etr_get_vc(Local), Vote, #exc{}, [], 0).
+send_decision({Env, Local}, Reply, Vote, VC) ->
+ send_decision({Env, Local}, Reply, VC, Vote, #exc{}, [], 0).
+send_decision(State, no_reply, VC, Vote, Exc) ->
+ send_decision(State, no_reply, VC, Vote, Exc, [], 0).
+
+%%-- Decision sent to all members. Do not reply (used when restarting).
+send_decision({Env, Local}, no_reply, [], _, #exc{mixed = true}, [], _) ->
+ {Env, ?etr_set_exc(Local, ?tr_mixed)};
+send_decision({Env, Local}, no_reply, [], _, #exc{hazard = true}, [], _) ->
+ {Env, ?etr_set_exc(Local, ?tr_hazard)};
+send_decision({Env, Local}, no_reply, [], _, _, [], _) ->
+ {Env, Local};
+send_decision({Env, Local}, no_reply, [], Vote, Exc, Failed, Times) ->
+ case ?tr_get_maxR(Env) of
+ Times ->
+ ?tr_error_msg("MAJOR ERROR, failed sending commit decision to: ~p. Tried ~p times.", [Failed,Times]),
+ {Env, ?etr_set_exc(Local, ?tr_hazard)};
+ _->
+ timer:sleep(?tr_get_maxW(Env)),
+ NewTimes = Times+1,
+ send_decision({Env, Local}, no_reply, Failed, Vote, Exc, [], NewTimes)
+ end;
+%%-- end special cases.
+
+%% Decision sent to all members. Test exceptions.
+send_decision({Env, Local}, Reply, [], Vote, Exc, [], _) ->
+ notify_subtrAware(Vote, ?etr_get_subAw(Local), ?etr_get_self(Local)),
+ test_exc(Exc, Vote, Reply, {Env, Local});
+%% Decision not sent to all members (one or more failed). Retry.
+send_decision({Env, Local}, Reply, [], Vote, Exc, Failed, Times) ->
+ case ?tr_get_maxR(Env) of
+ Times ->
+ ?tr_error_msg("MAJOR ERROR, failed sending commit decision to: ~p. Tried ~p times.", [Failed,Times]),
+ notify_subtrAware(Vote, ?etr_get_subAw(Local), ?etr_get_self(Local)),
+ test_exc(Exc#exc{hazard = true}, Vote, Reply, {Env, Local});
+ _->
+ NewTimes = Times+1,
+ timer:sleep(?tr_get_maxW(Env)),
+ send_decision({Env, Local}, Reply, Failed, Vote, Exc, [], NewTimes)
+ end;
+
+send_decision({Env, Local}, Reply, [Rhead|Rtail], Vote, Exc, Failed, Times) ->
+ ?debug_print("Coordinator:send_decision(~p) Try: ~p~n",[Vote, Times]),
+ case catch 'CosTransactions_Resource':Vote(Rhead) of
+ ok ->
+ ?etr_log(?tr_get_etrap(Env),{sent, Rhead}),
+ send_decision({Env, Local}, Reply, Rtail, Vote, Exc, Failed, Times);
+ {'EXCEPTION', E} when Vote == commit andalso
+ is_record(E, 'CosTransactions_NotPrepared') ->
+ ?debug_print("send_decision resource unprepared~n",[]),
+ case catch 'CosTransactions_Resource':prepare(Rhead) of
+ 'VoteCommit' ->
+ send_decision({Env, Local}, Reply, [Rhead|Rtail], Vote, Exc, Failed, Times);
+ 'VoteRollback' ->
+ send_decision({Env, Local}, Reply, Rtail, Vote, Exc#exc{mixed = true}, Failed, Times);
+ {'EXCEPTION', E} ->
+ {SetExc, NewL, DidFail} =
+ evaluate_answer(E, Rhead, Vote, Exc,
+ ?tr_get_etrap(Env), Local),
+ send_decision({Env, NewL}, Reply, Rtail, Vote, SetExc, DidFail++Failed, Times)
+ end;
+ {'EXCEPTION', E} ->
+ {SetExc, NewL, DidFail} =
+ evaluate_answer(E, Rhead, Vote, Exc, ?tr_get_etrap(Env), Local),
+ ?tr_error_msg("Resource:~p( ~p )~nRaised Exception: ~p~n",
+ [Vote, Rhead, E]),
+ send_decision({Env, NewL}, Reply, Rtail, Vote, SetExc, DidFail++Failed, Times);
+ {'EXIT', _} ->
+ send_decision({Env, Local}, Reply, Rtail,
+ Vote, Exc, [Rhead|Failed], Times);
+ Other ->
+ ?tr_error_msg("Resource:~p( ~p ) failed.~nREASON: ~p~n",
+ [Vote, Rhead, Other]),
+ case catch corba_object:non_existent(Rhead) of
+ true when Vote == commit ->
+ %% Presumed rollback
+ send_decision({Env, Local}, Reply, Rtail, Vote,
+ Exc#exc{mixed = true}, Failed, Times);
+ true ->
+ %% Presumed rollback
+ send_decision({Env, Local}, Reply, Rtail, Vote,
+ Exc#exc{hazard = true}, Failed, Times);
+ _ ->
+ send_decision({Env, Local}, Reply, Rtail,
+ Vote, Exc, [Rhead|Failed], Times)
+ end
+ end.
+
+%%-----------------------------------------------------------%
+%% function : notify_subtrAware,
+%% Arguments:
+%% Returns :
+%% Effect : Invoke an operation on a list of objects. We don't
+%% care about return values or exceptions.
+%%------------------------------------------------------------
+
+notify_subtrAware(commit, Resources, Self) ->
+ send_info(Resources, Self,
+ 'CosTransactions_SubtransactionAwareResource',
+ commit_subtransaction);
+notify_subtrAware(_, Resources, _) ->
+ send_info(Resources, 'CosTransactions_SubtransactionAwareResource',
+ rollback_subtransaction).
+
+%%-----------------------------------------------------------%
+%% function : send_info
+%% Arguments: ObjectList - List of object refernces to call.
+%% M - Module
+%% F - Function
+%% (Arg - required arguments)
+%% Returns : ok
+%% Effect : A lightweight function to be used when we don't
+%% "care" about the return value.
+%%------------------------------------------------------------
+
+send_info([], _, _, _) ->
+ ok;
+send_info([Rhead|Rtail], Arg, M, F) ->
+ ?debug_print("~p( ~p )~n",[F, Arg]),
+ case catch M:F(Rhead, Arg) of
+ {'EXIT',R} ->
+ ?tr_error_msg("~p:~p(~p, ~p) returned {'EXIT',~p}", [M,F,Rhead,Arg,R]);
+ {'EXCEPTION',E} ->
+ ?tr_error_msg("~p:~p(~p, ~p) returned {'EXCEPTION',~p}", [M,F,Rhead,Arg,E]);
+ _->
+ ok
+ end,
+ send_info(Rtail, Arg, M, F).
+
+send_info([], _, _) ->
+ ok;
+send_info([Rhead|Rtail], M, F) ->
+ ?debug_print("~p( )~n",[F]),
+ case catch M:F(Rhead) of
+ {'EXIT',R} ->
+ ?tr_error_msg("~p:~p(~p) returned {'EXIT',~p}", [M,F,Rhead,R]);
+ {'EXCEPTION',E} ->
+ ?tr_error_msg("~p:~p(~p) returned {'EXCEPTION',~p}", [M,F,Rhead,E]);
+ _->
+ ok
+ end,
+ send_info(Rtail, M, F).
+
+%%-----------------------------------------------------------%
+%% function : evaluate_answer
+%% Arguments:
+%% Returns :
+%% Effect : Check what kind of exception we received.
+%%------------------------------------------------------------
+
+evaluate_answer(E, Rhead, _Vote, Exc, Log, Local)
+ when is_record(E, 'CosTransactions_HeuristicMixed') ->
+ ?etr_log(Log, {heuristic, {Rhead, E}}),
+ {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []};
+evaluate_answer(E, Rhead, _Vote, Exc, Log, Local)
+ when is_record(E, 'CosTransactions_HeuristicHazard') ->
+ ?etr_log(Log, {heuristic, {Rhead, E}}),
+ {Exc#exc{hazard = true}, ?etr_add_raisedH(Local, Rhead), []};
+evaluate_answer(E, Rhead, Vote, Exc, Log, Local)
+ when is_record(E, 'CosTransactions_HeuristicCommit') ->
+ case Vote of
+ commit ->
+ ?etr_log(Log, {heuristic, {Rhead, E}}),
+ {Exc, ?etr_add_raisedH(Local, Rhead), []};
+ _->
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_mixed}}),
+ {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []}
+ end;
+evaluate_answer(E, Rhead, Vote, Exc, Log, Local)
+ when is_record(E, 'CosTransactions_HeuristicRollback')->
+ case Vote of
+ rollback ->
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_rollback}}),
+ {Exc, ?etr_add_raisedH(Local, Rhead), []};
+ _->
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_mixed}}),
+ {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []}
+ end;
+evaluate_answer(E, Rhead, Vote, Exc, Log, Local)
+ when Vote == commit andalso is_record(E, 'TRANSACTION_ROLLEDBACK') ->
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_mixed}}),
+ {Exc#exc{mixed = true}, ?etr_add_raisedH(Local, Rhead), []};
+evaluate_answer(E, Rhead, Vote, Exc, Log, Local) when is_record(E, 'TIMEOUT') ->
+ ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
+ [Vote, Rhead, E]),
+ case catch corba_object:non_existent(Rhead) of
+ true ->
+ %% Since we have presumed abort, the child will
+ %% assume rollback if this server do not exist any more.
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
+ {Exc#exc{hazard = true}, Local, []};
+ _ ->
+ {Exc, Local, [Rhead]}
+ end;
+evaluate_answer(E, Rhead, Vote, Exc, Log, Local) when is_record(E, 'TRANSIENT') ->
+ ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
+ [Vote, Rhead, E]),
+ case catch corba_object:non_existent(Rhead) of
+ true ->
+ %% Since we have presumed abort, the child will
+ %% assume rollback if this server do not exist any more.
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
+ {Exc#exc{hazard = true}, Local, []};
+ _ ->
+ {Exc, Local, [Rhead]}
+ end;
+evaluate_answer(E, Rhead, Vote, Exc, Log, Local) when is_record(E, 'COMM_FAILURE') ->
+ ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
+ [Vote, Rhead, E]),
+ case catch corba_object:non_existent(Rhead) of
+ true ->
+ %% Since we have presumed abort, the child will
+ %% assume rollback if this server do not exist any more.
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
+ {Exc#exc{hazard = true}, Local, []};
+ _ ->
+ {Exc, Local, [Rhead]}
+ end;
+evaluate_answer(E, Rhead, Vote, Exc, Log, Local)when is_record(E, 'OBJECT_NOT_EXIST') ->
+ ?tr_error_msg("Coordinator:~p( ~p ) Object unreachable.~nReason: ~p~n",
+ [Vote, Rhead, E]),
+ %% Since we have presumed abort, the child will
+ %% assume rollback if this server do not exist any more.
+ ?etr_log(Log, {heuristic, {Rhead, ?tr_hazard}}),
+ {Exc#exc{hazard = true}, Local, []};
+evaluate_answer(Unknown, Rhead, Vote, Exc, _Log, Local)->
+ ?tr_error_msg("Coordinator:~p( ~p ). Unknown reply: ~p.~n",
+ [Vote, Rhead, Unknown]),
+ {Exc, Local, []}.
+
+
+%%-----------------------------------------------------------%
+%% function : test_exc
+%% Arguments: Exc - instance of #exc{} locally declared.
+%% Vote - 'rollback' or 'commit'
+%% Reply - If no exceptions this is the default reply.
+%% State - genserver state
+%% Returns :
+%% Effect : Raise the correct exception or simply reply to
+%% the genserver. NOTE that the testing for exceptions
+%% differs if we are performing a rollback or commit.
+%% Check if Mixed first; takes priority over Hazard.
+%% HeuristicRollback and VoteCommit together give
+%% HeuristicMixed
+%% HeuristicCommit and VoteRollback together give
+%% HeuristicMixed
+%%------------------------------------------------------------
+
+test_exc(#exc{mixed = true}, _, _, {Env, Local}) ->
+ {reply, {'EXCEPTION', ?tr_mixed}, {Env, ?etr_set_exc(Local, ?tr_mixed)}};
+% Left out for now to avoid dialyzer warning.
+%test_exc(#exc{rollback = true}, commit, _, {Env, Local}) ->
+% {reply, {'EXCEPTION', ?tr_mixed}, {Env, ?etr_set_exc(Local, ?tr_mixed)}};
+% Left out for now to avoid dialyzer warning.
+%test_exc(#exc{commit = true}, rollback, _, {Env, Local}) ->
+% {reply, {'EXCEPTION', ?tr_mixed}, {Env, ?etr_set_exc(Local, ?tr_mixed)}};
+test_exc(#exc{hazard = true}, _, _, {Env, Local}) ->
+ {reply, {'EXCEPTION', ?tr_hazard}, {Env, ?etr_set_exc(Local, ?tr_hazard)}};
+test_exc(_, _, {'EXCEPTION', E}, {Env, Local})
+ when is_record(E, 'TRANSACTION_ROLLEDBACK')->
+ {stop, normal, {'EXCEPTION', E}, {Env, Local}};
+%% Replace the case above if allow synchronization
+%test_exc(_, _, {'EXCEPTION', E}, {Env, Local})
+% when record(E, 'TRANSACTION_ROLLEDBACK')->
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, {'EXCEPTION', E}, {Env, Local}};
+% _->
+% {reply, {'EXCEPTION', E}, {Env, Local}}
+% end;
+test_exc(_, _, {'EXCEPTION', E}, State) ->
+ {reply, {'EXCEPTION', E}, State};
+test_exc(_, _, Reply, {Env, Local}) ->
+ {stop, normal, Reply, {Env, Local}}.
+%% Replace the case above if allow synchronization
+%test_exc(_, _, Reply, {Env, Local}) ->
+% case ?etr_get_sync(Local) of
+% [] ->
+% {stop, normal, Reply, {Env, Local}};
+% _ ->
+% {reply, Reply, {Env, Local}}
+% end.
+
+%%-----------------------------------------------------------%
+%% function : evaluate_status
+%% Arguments:
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+evaluate_status(Status) ->
+ case Status of
+ 'StatusCommitted' -> commit;
+ 'StatusCommitting' -> commit;
+ 'StatusMarkedRollback' -> rollback;
+ 'StatusRollingBack' -> rollback;
+ 'StatusRolledBack' -> rollback;
+ 'StatusActive' -> rollback;
+ 'StatusPrepared' -> rollback;
+ 'StatusUnknown' -> rollback;
+ 'StatusNoTransaction' -> rollback;
+ 'StatusPreparing' -> rollback;
+ _-> rollback
+ end.
+
+
+%%-----------------------------------------------------------%
+%% function : prepare_restart
+%% Arguments:
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+%% The file contains no data. The coordinator crashed before
+%% a prepare-call was made. Presumed rollback.
+prepare_restart(State, eof) ->
+ ?debug_print("prepare_restart: eof, init~n",[]),
+ self() ! {suicide, self()},
+ {ok, State};
+%% Collected all necessary votes. Do commit_restart.
+prepare_restart({Env, _}, {{pre_vote, _Vote, Data}, Cursor}) ->
+ ?debug_print("prepare_restart: pre_vote( ~p )~n",[_Vote]),
+ if
+ ?tr_is_root(Env) ->
+ commit_restart({Env, Data},
+ ?etr_read(?tr_get_etrap(Env), Cursor), root);
+ true ->
+ commit_restart({Env, Data},
+ ?etr_read(?tr_get_etrap(Env), Cursor), subCoord)
+ end;
+%% 'rollback' called without 'prepare'. This case occurs if the Coordinator
+%% crashes when send_info or notify_subtrAware.
+prepare_restart({Env, _}, {{rollback, NewL}, _Cursor}) ->
+ ?debug_print("prepare_restart: pre_vote( rollback )~n",[]),
+ send_info(?etr_get_members(NewL), 'CosTransactions_Resource', rollback),
+ notify_subtrAware(rollback, ?etr_get_subAw(NewL), ?etr_get_self(NewL)),
+ self() ! {suicide, self()},
+ {ok, {Env, NewL}};
+%% Something is wrong in the log.
+prepare_restart(_, _) ->
+ ?tr_error_msg("Internal log read failed:~n", []),
+ {stop, {error, "restart failed"}}.
+
+%%-----------------------------------------------------------%
+%% function : commit_restart
+%% Arguments: Env - server context
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+commit_restart({Env, Local}, Data, Phase) ->
+ Exc = set_exception(#exc{}, ?etr_get_exc(Local)),
+ commit_restart({Env, Local}, Data, Phase, Exc).
+
+%% Normal case. No errors no exceptions.
+commit_restart({Env, Local}, {{sent, Obj}, Cursor}, Vote, Exc) ->
+ ?debug_print("commit_restart: sent~n",[]),
+ commit_restart({Env, ?etr_remove_vc(Local, Obj)},
+ ?etr_read(?tr_get_etrap(Env), Cursor), Vote, Exc);
+commit_restart({Env, Local}, {{heuristic, {Obj,E}}, Cursor}, Vote, Exc) ->
+ ?debug_print("commit_restart: heuristic ~p~n",[E]),
+ NewExc = set_exception(Exc, E),
+ commit_restart({Env, ?etr_add_raisedH(Local, Obj)},
+ ?etr_read(?tr_get_etrap(Env), Cursor), Vote, NewExc);
+
+
+%% --- cases which only can occure once in the log ------------
+
+%% The file contains no data. The coordinator crashed before
+%% a decision was made. Causes rollback.
+commit_restart({E, L}, eof, root, Exc) ->
+ ?debug_print("commit_restart: eof init (root only)~n",[]),
+ {Env, Local} = send_decision({E, L}, no_reply, ?etr_get_vc(L),
+ rollback, Exc),
+ exception_set({Env, Local});
+%% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% exception_set({Env, Local});
+% SynchObjs ->
+% {ok, {Env, Local}}
+% end;
+
+
+%% Passed the prepare_restart. Not received a commit decision from the
+%% parent.
+commit_restart({E, L}, eof, subCoord, Exc) ->
+ ?debug_print("commit_restart: eof init (subcoord only)~n",[]),
+ case catch corba_object:non_existent(?tr_get_parent(E)) of
+ true ->
+ %% Presumed rollback.
+ {Env, Local} = send_decision({E, L}, no_reply, ?etr_get_vc(L),
+ rollback, Exc),
+ self() ! {suicide, self()},
+ {ok, {Env, Local}};
+%% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% self() ! {suicide, self()},
+% {ok, {Env, Local}};
+% SynchObjs ->
+% case ?tr_get_parents(Env) of
+% [] ->
+% send_info(SynchObjs, ?etr_get_status(Local),
+% 'CosTransactions_Synchronization', after_completion);
+% _->
+% ok
+% end,
+% self() ! {suicide, self()},
+% {ok, {Env, Local}}
+% end;
+ _->
+ {ok, {E, L}}
+ end;
+
+commit_restart({Env, Local}, eof, Vote, Exc) ->
+ ?debug_print("commit_restart: eof VOTE: ~p~n",[Vote]),
+ case ?etr_get_vc(Local) of
+ [] ->
+ ?debug_print("commit_restart: all sent, test exc~n",[]),
+ exception_set({Env, Local});
+ VC ->
+ ?debug_print("commit_restart: note done. send more~n",[]),
+ State = send_decision({Env, Local}, no_reply, VC, Vote, Exc),
+ exception_set(State)
+ end;
+
+%% Decision made, i.e. rollback or commit.
+commit_restart({Env, Local}, {rollback, Cursor}, _Phase, Exc) ->
+ ?debug_print("commit_restart: decided rollback~n",[]),
+ commit_restart({Env, ?etr_set_status(Local, 'StatusRolledBack')},
+ ?etr_read(?tr_get_etrap(Env), Cursor), rollback, Exc);
+commit_restart({Env, Local}, {commit, Cursor}, _Phase, Exc) ->
+ ?debug_print("commit_restart: decided commit~n",[]),
+ commit_restart({Env, ?etr_set_status(Local, 'StatusCommitted')},
+ ?etr_read(?tr_get_etrap(Env), Cursor), commit, Exc);
+commit_restart({Env, Local}, {forget_phase, Cursor}, _, _) ->
+ ?debug_print("commit_restart: start sending forget~n",[]),
+ forget_restart({Env, Local}, ?etr_read(?tr_get_etrap(Env), Cursor));
+
+commit_restart({_Env, _Local}, _R, _, _) ->
+ ?debug_print("RESTART FAIL: ~p~n",[_R]),
+ ?tr_error_msg("Internal log read failed:~n", []),
+ exit("restart failed").
+
+%%-----------------------------------------------------------%
+%% function : forget_restart
+%% Arguments: {Env, Local} - server context
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+%% Exception logged. Test if we issued a 'forget()' to the Resource.
+forget_restart({Env, Local}, eof) ->
+ case ?etr_get_raisedH(Local) of
+ [] ->
+ ?debug_print("forget_restart: all done~n",[]);
+ Left ->
+ ?debug_print("forget_restart: not done. send more~n",[]),
+ send_forget(Left, ?tr_get_etrap(Env))
+ end,
+ self() ! {suicide, self()},
+ {ok, {Env, Local}};
+%% Replace the reply above if allow synchronization
+% case ?etr_get_sync(Local) of
+% [] ->
+% self() ! {suicide, self()},
+% {ok, {Env, Local}};
+% SynchObjs ->
+% case ?tr_get_parents(Env) of
+% [] ->
+% send_info(SynchObjs, ?etr_get_status(Local),
+% 'CosTransactions_Synchronization', after_completion),
+% self() ! {suicide, self()},
+% {ok, {Env, Local}};
+% _->
+% {ok, {Env, Local}}
+% end
+% end;
+forget_restart({Env, Local}, {{forgotten, Obj}, Cursor}) ->
+ ?debug_print("forget_restart: forgotten heuristic~n",[]),
+ NewL = ?etr_remove_raisedH(Local, Obj),
+ forget_restart({Env, NewL}, ?etr_read(?tr_get_etrap(Env), Cursor));
+forget_restart({Env, Local}, {{not_forgotten, Obj}, Cursor}) ->
+ ?debug_print("forget_restart: not_forgotten~n",[]),
+ NewL = ?etr_remove_raisedH(Local, Obj),
+ send_forget([Obj], dummy),
+ forget_restart({Env, NewL}, ?etr_read(?tr_get_etrap(Env), Cursor)).
+
+%%--------------- END OF MODULE ------------------------------
diff --git a/lib/cosTransactions/src/Makefile b/lib/cosTransactions/src/Makefile
new file mode 100644
index 0000000000..7e10ec175b
--- /dev/null
+++ b/lib/cosTransactions/src/Makefile
@@ -0,0 +1,175 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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%
+#
+#
+include $(ERL_TOP)/make/target.mk
+EBIN=../ebin
+
+ifeq ($(TYPE),debug)
+ERL_COMPILE_FLAGS += -Ddebug -W
+endif
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(COSTRANSACTIONS_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/cosTransactions-$(VSN)
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES = \
+ ETraP_Common \
+ etrap_logmgr \
+ ETraP_Server_impl \
+ CosTransactions_Terminator_impl \
+ CosTransactions_TransactionFactory_impl \
+ cosTransactions
+
+ERL_FILES = $(MODULES:%=%.erl)
+HRL_FILES = \
+ ETraP_Common.hrl
+
+GEN_ERL_FILES = \
+ oe_CosTransactions.erl \
+ CosTransactions_Control.erl \
+ CosTransactions_Coordinator.erl \
+ CosTransactions_HeuristicCommit.erl \
+ CosTransactions_HeuristicHazard.erl \
+ CosTransactions_HeuristicMixed.erl \
+ CosTransactions_HeuristicRollback.erl \
+ CosTransactions_Inactive.erl \
+ CosTransactions_InvalidControl.erl \
+ CosTransactions_NoTransaction.erl \
+ CosTransactions_NotPrepared.erl \
+ CosTransactions_NotSubtransaction.erl \
+ CosTransactions_RecoveryCoordinator.erl \
+ CosTransactions_Resource.erl \
+ CosTransactions_SubtransactionAwareResource.erl \
+ CosTransactions_SubtransactionsUnavailable.erl \
+ CosTransactions_Terminator.erl \
+ CosTransactions_TransactionFactory.erl \
+ CosTransactions_Unavailable.erl \
+ CosTransactions_SynchronizationUnavailable.erl \
+ CosTransactions_TransIdentity.erl \
+ CosTransactions_PropagationContext.erl \
+ CosTransactions_otid_t.erl \
+ CosTransactions_WrongTransaction.erl \
+ ETraP_Server.erl
+# CosTransactions_Synchronization.erl \
+
+EXTERNAL_INC_PATH = ../include
+
+GEN_HRL_FILES = \
+ oe_CosTransactions.hrl \
+ CosTransactions.hrl \
+ CosTransactions_Control.hrl \
+ CosTransactions_Coordinator.hrl \
+ CosTransactions_RecoveryCoordinator.hrl \
+ CosTransactions_Resource.hrl \
+ CosTransactions_SubtransactionAwareResource.hrl \
+ CosTransactions_Terminator.hrl \
+ CosTransactions_TransactionFactory.hrl \
+ ETraP.hrl \
+ ETraP_Server.hrl
+# CosTransactions_Synchronization.hrl \
+
+EXTERNAL_GEN_HRL_FILES = $(GEN_HRL_FILES:%=$(EXTERNAL_INC_PATH)/%)
+
+GEN_FILES = $(GEN_ERL_FILES) $(EXTERNAL_GEN_HRL_FILES)
+
+TARGET_FILES = \
+ $(GEN_ERL_FILES:%.erl=$(EBIN)/%.$(EMULATOR)) \
+ $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+IDL_FILE = \
+ CosTransactions.idl
+
+APPUP_FILE = cosTransactions.appup
+APPUP_SRC = $(APPUP_FILE).src
+APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
+
+APP_FILE = cosTransactions.app
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_IDL_FLAGS += -pa $(ERL_TOP)/lib/cosTransactions/ebin \
+ -pa $(ERL_TOP)/lib/ic/ebin\
+ -pa $(ERL_TOP)/lib/orber/ebin
+
+# The -pa option is just used temporary until erlc can handle
+# includes from other directories than ../include .
+ERL_COMPILE_FLAGS += \
+ $(ERL_IDL_FLAGS) \
+ -I$(ERL_TOP)/lib/cosTransactions/include \
+ -I$(ERL_TOP)/lib/orber/include \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,"cosTransactions_$(COSTRANSACTIONS_VSN)"}'
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+
+debug:
+ @${MAKE} TYPE=debug
+
+clean:
+ rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f errs core *~
+
+$(APP_TARGET): $(APP_SRC)
+ sed -e 's;%VSN%;$(VSN);' $(APP_SRC) > $(APP_TARGET)
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+docs:
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+$(GEN_ERL_FILES) $(EXTERNAL_GEN_HRL_FILES): CosTransactions.idl
+ erlc $(ERL_IDL_FLAGS) +'{cfgfile,"CosTransactions.cfg"}' CosTransactions.idl
+ mv $(GEN_HRL_FILES) $(EXTERNAL_INC_PATH)
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILE) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) $(RELSYSDIR)/include
+
+release_docs_spec:
diff --git a/lib/cosTransactions/src/cosTransactions.app.src b/lib/cosTransactions/src/cosTransactions.app.src
new file mode 100644
index 0000000000..52769b1711
--- /dev/null
+++ b/lib/cosTransactions/src/cosTransactions.app.src
@@ -0,0 +1,43 @@
+{application, cosTransactions,
+ [{description, "The Erlang CosTransactions application"},
+ {vsn, "%VSN%"},
+ {modules,
+ [
+ etrap_logmgr,
+ 'ETraP_Server_impl',
+ 'CosTransactions_Terminator_impl',
+ 'CosTransactions_TransactionFactory_impl',
+ 'ETraP_Common',
+ oe_CosTransactions,
+ 'CosTransactions_Control',
+ 'CosTransactions_Coordinator',
+ 'CosTransactions_HeuristicCommit',
+ 'CosTransactions_HeuristicHazard',
+ 'CosTransactions_HeuristicMixed',
+ 'CosTransactions_HeuristicRollback',
+ 'CosTransactions_Inactive',
+ 'CosTransactions_InvalidControl',
+ 'CosTransactions_NoTransaction',
+ 'CosTransactions_NotPrepared',
+ 'CosTransactions_NotSubtransaction',
+ 'CosTransactions_RecoveryCoordinator',
+ 'CosTransactions_Resource',
+ 'CosTransactions_SubtransactionAwareResource',
+ 'CosTransactions_SubtransactionsUnavailable',
+ 'CosTransactions_Terminator',
+ 'CosTransactions_TransactionFactory',
+ 'CosTransactions_Unavailable',
+ 'CosTransactions_SynchronizationUnavailable',
+ 'CosTransactions_TransIdentity',
+ 'CosTransactions_PropagationContext',
+ 'CosTransactions_otid_t',
+ 'CosTransactions_WrongTransaction',
+ 'ETraP_Server',
+ cosTransactions
+ ]
+ },
+ {registered, [cosTransactions_sup, oe_cosTransactionsFactory]},
+ {applications, [orber, stdlib, kernel]},
+ {env, []},
+ {mod, {cosTransactions, []}}
+]}.
diff --git a/lib/cosTransactions/src/cosTransactions.appup.src b/lib/cosTransactions/src/cosTransactions.appup.src
new file mode 100644
index 0000000000..f3eead4a0c
--- /dev/null
+++ b/lib/cosTransactions/src/cosTransactions.appup.src
@@ -0,0 +1,6 @@
+{"%VSN%",
+ [
+ ],
+ [
+ ]
+}.
diff --git a/lib/cosTransactions/src/cosTransactions.erl b/lib/cosTransactions/src/cosTransactions.erl
new file mode 100644
index 0000000000..ffc01f50a4
--- /dev/null
+++ b/lib/cosTransactions/src/cosTransactions.erl
@@ -0,0 +1,115 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : cosTransactions.erl
+%% Purpose : Initialize the 'cosTransactions' application
+%%----------------------------------------------------------------------
+
+-module(cosTransactions).
+
+%%--------------- INCLUDES -----------------------------------
+%% Local
+-include_lib("ETraP_Common.hrl").
+-include_lib("CosTransactions.hrl").
+%%--------------- EXPORTS-------------------------------------
+%% cosTransactions API external
+-export([start/0, stop/0, start_factory/1, start_factory/0, stop_factory/1]).
+
+%% Application callbacks
+-export([start/2, init/1, stop/1]).
+
+%%------------------------------------------------------------
+%% function : start/stop
+%% Arguments:
+%% Returns :
+%% Effect : Starts or stops the cosTRansaction application.
+%%------------------------------------------------------------
+
+start() ->
+ application:start(cosTransactions).
+stop() ->
+ application:stop(cosTransactions).
+
+%%------------------------------------------------------------
+%% function : start_factory
+%% Arguments: none or an argumentlist which by default is defined
+%% in ETraP_Common.hrl, i.e., '?tr_FAC_DEF'
+%% Returns : ObjectRef | {'EXCEPTION', _} | {'EXIT', Reason}
+%% Effect : Starts a CosTransactions_TransactionFactory
+%%------------------------------------------------------------
+
+start_factory() ->
+ ?tr_start_child(?SUP_FAC(?tr_FAC_DEF)).
+
+start_factory(Args) when is_list(Args) ->
+ ?tr_start_child(?SUP_FAC(Args));
+start_factory(Args) ->
+ ?tr_error_msg("applications:start( ~p ) failed. Bad parameters~n", [Args]),
+ exit("applications:start failed. Bad parameters").
+
+%%------------------------------------------------------------
+%% function : stop_factory
+%% Arguments: Factory Object Reference
+%% Returns : ok | {'EXCEPTION', _}
+%% Effect :
+%%------------------------------------------------------------
+
+stop_factory(Fac)->
+ corba:dispose(Fac).
+
+%%------------------------------------------------------------
+%% function : start
+%% Arguments: Type - see module application
+%% Arg - see module application
+%% Returns :
+%% Effect : Module callback for application
+%%------------------------------------------------------------
+
+start(_, _) ->
+ supervisor:start_link({local, ?SUPERVISOR_NAME}, cosTransactions, app_init).
+
+
+%%------------------------------------------------------------
+%% function : stop
+%% Arguments: Arg - see module application
+%% Returns :
+%% Effect : Module callback for application
+%%------------------------------------------------------------
+
+stop(_) ->
+ ok.
+
+%%------------------------------------------------------------
+%% function : init
+%% Arguments:
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+%% Starting using create_factory/X
+init(own_init) ->
+ {ok,{?SUP_FLAG, [?SUP_CHILD]}};
+%% When starting as an application.
+init(app_init) ->
+ {ok,{?SUP_FLAG, [?SUP_CHILD]}}.
+
+
+%%--------------- END OF MODULE ------------------------------
diff --git a/lib/cosTransactions/src/etrap_logmgr.erl b/lib/cosTransactions/src/etrap_logmgr.erl
new file mode 100644
index 0000000000..a418d782e3
--- /dev/null
+++ b/lib/cosTransactions/src/etrap_logmgr.erl
@@ -0,0 +1,200 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : etrap_logmgr.erl
+%% Purpose : Make it easier to use disk_log.
+%%----------------------------------------------------------------------
+
+-module(etrap_logmgr).
+
+%%--------------- INCLUDES -----------------------------------
+%% Local
+-include_lib("ETraP_Common.hrl").
+%%--------------- IMPORTS-------------------------------------
+%%--------------- EXPORTS-------------------------------------
+-export([start/1, stop/1, log_safe/2, log_lazy/2, get_next/2]).
+
+
+%%------------------------------------------------------------
+%% function : start
+%% Arguments: LogName - name of the disk_log.
+%% Returns :
+%% Effect : creating linked log
+%%------------------------------------------------------------
+
+start(LogName) ->
+ case catch disk_log:open([{name, LogName},
+ {file, LogName},
+ {type, halt},
+ {size, infinity}]) of
+ {ok, LogName} ->
+ ok;
+ {error, Reason} ->
+ ?tr_error_msg("Initiating internal log failed: ~p", [Reason]),
+ exit({error, Reason});
+ {repaired, LogName, {recovered, _Rec}, {badbytes, _Bad}} ->
+ ok;
+ Other ->
+ ?tr_error_msg("Initiating internal log failed: ~p", [Other]),
+ exit({error, Other})
+ end.
+
+%%------------------------------------------------------------
+%% function : stop
+%% Arguments: LogName - name of the disk_log.
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+
+stop(LogName) ->
+ case catch disk_log:close(LogName) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ ?tr_error_msg("Stopping internal log failed: ~p", [Reason]),
+ {error, Reason};
+ Other ->
+ ?tr_error_msg("Stopping internal log failed: ~p", [Other]),
+ {error, Other}
+ end.
+
+
+%%------------------------------------------------------------
+%% function : log_safe
+%% Arguments: LogName - name of the disk_log. If 'dummy' is
+%% used nothing should be logged. Reason, reuse code.
+%% LogRecord - record to store in the log.
+%% Returns :
+%% Effect : Writes a logrecord and synchronizes to make sure
+%% that the record is stored.
+%%------------------------------------------------------------
+
+log_safe(dummy, _) ->
+ ok;
+log_safe(LogName, LogRecord) ->
+ case write_safe(LogName, LogRecord) of
+ ok ->
+ ok;
+ _ ->
+ %% We have to catch the exit because in some cases
+ %% it's not possible to abort action in the 2PC-protocol.
+ case catch start(LogName) of
+ ok ->
+ write_safe(LogName, LogRecord);
+ {'EXIT', Reason} ->
+ {error, Reason}
+ end
+ end.
+
+
+write_safe(LogName, LogRecord) ->
+ case catch disk_log:log(LogName, LogRecord) of
+ ok -> % wrote to kernel successfully
+ case catch disk_log:sync(LogName) of
+ ok -> % Written to disk successfully
+ ok;
+ {error, Reason} ->
+ ?tr_error_msg("Internal log write failed: ~p ~p",
+ [Reason, LogName]),
+ {error, Reason};
+ Other ->
+ ?tr_error_msg("Internal log write failed: ~p ~p",
+ [Other, LogName]),
+ {error, Other}
+ end;
+ {error, Reason} ->
+ ?tr_error_msg("Internal log write failed: ~p ~p", [Reason, LogName]),
+ {error, Reason};
+ Other ->
+ ?tr_error_msg("Internal log write failed: ~p ~p", [Other, LogName]),
+ {error, Other}
+ end.
+
+
+%%------------------------------------------------------------
+%% function : log_lazy
+%% Arguments: LogName - name of the disk_log. If 'dummy' is
+%% used nothing should be logged. Reason, reuse code.
+%% LogRecord - record to store in the log.
+%% Returns :
+%% Effect : Writes a logrecord. The record may be lost.
+%%------------------------------------------------------------
+
+log_lazy(dummy, _LogRecord) ->
+ ok;
+log_lazy(LogName, LogRecord) ->
+ case write_lazy(LogName, LogRecord) of
+ ok ->
+ ok;
+ _ ->
+ %% We have to catch the exit because in some cases
+ %% it's not possible to abort action in the 2PC-protocol.
+ case catch start(LogName) of
+ ok ->
+ write_lazy(LogName, LogRecord);
+ {'EXIT', Reason} ->
+ {error, Reason}
+ end
+ end.
+
+write_lazy(LogName, LogRecord) ->
+ case catch disk_log:log(LogName, LogRecord) of
+ ok ->
+ %% wrote to kernel successfully
+ ok;
+ {error, Reason} ->
+ %% Write to kernel failed with Reason
+ ?tr_error_msg("Internal log write failed: ~p", [Reason]),
+ {error, Reason};
+ Other ->
+ %% unknown message received.
+ ?tr_error_msg("Internal log write failed: ~p", [Other]),
+ {error, Other}
+ end.
+
+
+%%------------------------------------------------------------
+%% function : get_next
+%% Arguments: LogName - name of the disk_log.
+%% Cursor - place to read from.
+%% Returns : {Cursor, LogRecs} - A cursor and up to N logrecords.
+%% eof - the atom 'eof', indicating logfile empty.
+%% {error, Reason} - error.
+%% Effect :
+%% Purpose : Used when performing a REDO scan
+%%------------------------------------------------------------
+
+get_next(LogName, Cursor) ->
+ case catch disk_log:chunk(LogName, Cursor, 1) of
+ {NewCursor, [Data]} ->
+ {Data, NewCursor};
+ eof ->
+ eof;
+ {error, Reason} ->
+ ?tr_error_msg("Internal log '~p' read failed: ~p",
+ [LogName, Reason]),
+ exit({error, Reason});
+ _Other ->
+ ?tr_error_msg("Internal log '~p' read failed: 'log_corrupt'", [LogName]),
+ exit({error, "log_corrupt"})
+ end.
+
+%%--------------- END OF MODULE ------------------------------