File Coverage

blib/lib/Data/Conveyor/Environment.pm
Criterion Covered Total %
statement 107 150 71.3
branch 0 20 0.0
condition 1 21 4.7
subroutine 36 45 80.0
pod 15 15 100.0
total 159 251 63.3


line stmt bran cond sub pod time code
1 1     1   61242 use 5.008;
  1         4  
  1         42  
2 1     1   6 use strict;
  1         1  
  1         33  
3 1     1   4 use warnings;
  1         3  
  1         64  
4              
5             package Data::Conveyor::Environment;
6             BEGIN {
7 1     1   23 $Data::Conveyor::Environment::VERSION = '1.103130';
8             }
9             # ABSTRACT: Stage-based conveyor-belt-like ticket handling system
10              
11             # ptags: DCE
12 1     1   14 use Error::Hierarchy::Util qw/assert_defined load_class/;
  1         2  
  1         81  
13 1     1   806 use Class::Scaffold::Util 'const';
  1         893  
  1         50  
14 1     1   8 use Class::Scaffold::Factory::Type;
  1         1  
  1         8  
15 1     1   733 use Class::Value;
  1         28082  
  1         22  
16 1     1   696 use Data::Conveyor::Control::File; # object() doesn't load the class (?).
  1         3  
  1         17  
17 1     1   893 use Hook::Modular;
  1         34806  
  1         12  
18 1     1   832 use once;
  1         301  
  1         51  
19              
20             # Bring in Class::Value right now, so $Class::Value::SkipChecks can be set
21             # without it being overwritten, since with framework_object and
22             # make_obj() Class::Value is loaded only on-demand.
23 1     1   5 use parent 'Class::Scaffold::Environment';
  1         3  
  1         6  
24             Class::Scaffold::Base->add_autoloaded_package('Data::Conveyor::');
25             Class::Scaffold::Environment::gen_class_hash_accessor('STAGE');
26              
27             # ptags: /(\bconst\b[ \t]+(\w+))/
28             __PACKAGE__->mk_object_accessors(
29             'Data::Conveyor::Control::File' => 'control',
30             'Property::Lookup' => {
31             slot => 'configurator',
32             comp_mthds => [
33             qw(
34             max_tickets_per_dispatcher
35             dispatcher_sleep
36             lockpath
37             ignore_locks
38             soap_server
39             soap_path
40             soap_uri
41             mutex_storage_name
42             mutex_storage_args
43             respect_mutex
44             should_send_mail
45             default_object_limit
46             control_filename
47             ticket_provider_clause
48             storage_init_location
49             )
50             ]
51             },
52             );
53 1     1   156 use constant MUTEX_STORAGE_TYPE => 'mutex_storage';
  1         2  
  1         59  
54 1     1   4 use constant PAYLOAD_VERSION => 1;
  1         2  
  1         57  
55 1   33     172 use constant DEFAULTS => (
56             test_mode => (defined $ENV{TEST_MODE} && $ENV{TEST_MODE} == 1),
57              
58             # default_object_limit => 250,
59 1     1   5 );
  1         1  
60              
61             sub init {
62 2     2 1 2228 my $self = shift;
63 2         19 $self->SUPER::init(@_);
64 2         22 $self->multiplex_transaction_omit(MUTEX_STORAGE_TYPE() => 1);
65             ONCE {
66              
67             # require NEXT; as long as the patched NEXT.pm is in Data::Inherited -
68             # i.e., until such time as Damian releases the new version, we do:
69 1     1   22 require Data::Inherited;
70              
71             # generically generate instruction classes that look like:
72             # package D::C::Ticket::Payload::Instruction::value_person_organization;
73             # use parent 'Data::Conveyor::Ticket::Payload::Instruction';
74             # __PACKAGE__->mk_framework_object_accessors(
75             # value_person_organization => 'value'
76             # );
77             # use constant name => 'organization';
78             # There are other, more specialized instruction classes like 'clear'
79             # or those creating techdata items - which contain several value
80             # objects, not just one. There should be one instruction class for
81             # every unit that can be added, deleted or updated. A person's
82             # organization can be changed by itself, so we have an instruction for
83             # that. However, a techdata item can only be changed as a whole - you
84             # can't change a techdata item's individual field -, so we have one
85             # instruction for the whole techdata item.
86             # make sure the superclass is loaded so we can inherit from it
87 1         16 load_class $self->INSTRUCTION_CLASS_BASE(), 1;
88 1         14 for my $type ($self->generic_instruction_classes) {
89              
90             # construct instruction class
91 0         0 my $class = $self->INSTRUCTION_CLASS_BASE() . '::' . $type;
92 1     1   6 no strict 'refs';
  1         2  
  1         588  
93 0         0 push @{"$class\::ISA"} => $self->INSTRUCTION_CLASS_BASE;
  0         0  
94 0         0 my $type_method = "$class\::type";
95 0 0       0 $::PTAGS && $::PTAGS->add_tag('type', __FILE__, __LINE__ + 1);
96 0         0 *$type_method = sub { $type };
  0         0  
97              
98             # the class gets a $VERSION so that load_class() doesn't attempt
99             # to load it, q.v. We also make an entry in %INC so
100             # UNIVERSAL::require is happy. load_class() and require() could
101             # be called for this class in Data::Comparable.
102 0 0       0 $::PTAGS && $::PTAGS->add_tag('value', __FILE__, __LINE__ + 3);
103 0         0 eval qq!
104             package $class;
105             __PACKAGE__->mk_framework_object_accessors($type => 'value');
106             our \$VERSION = '0.01';
107             !;
108 0         0 my $file = $class . '.pm';
109 0         0 $file =~ s!::!/!g;
110 0         0 $INC{$file} = 1;
111 0 0       0 die $@ if $@;
112             }
113 2         57 };
114             }
115              
116             sub generic_instruction_classes {
117 1     1 1 3 my $self = shift;
118 1         5 $self->every_list('INSTRUCTION_CLASS_LIST');
119             }
120              
121             sub truth {
122 0     0 1 0 my ($self, $condition) = @_;
123 0 0       0 $condition ? $self->YES : $self->NO;
124             }
125              
126             # locks
127             const LO => (
128             LO_READ => 'read',
129             LO_WRITE => 'write',
130             );
131              
132             # YAML::Active phases
133             const YAP => (YAP_MAKE_TICKET => 'make_ticket',);
134              
135             # exception ignore
136             const EI => ();
137              
138             # context
139             const CTX => (
140             CTX_BEFORE => 'before',
141             CTX_AFTER => 'after',
142             );
143              
144             # ticket types
145             const TT => ();
146              
147             # ticket status
148             const TS => (
149             TS_RUNNING => 'R',
150             TS_HOLD => 'H',
151             TS_ERROR => 'E',
152             TS_DONE => 'D',
153             TS_PENDING => 'P',
154             );
155              
156             # tx status
157             const TXS => (
158             TXS_RUNNING => 'R',
159             TXS_IGNORE => 'I',
160             TXS_ERROR => 'E',
161             );
162              
163             # tx necessity
164             const TXN => (
165             TXN_MANDATORY => 'M',
166             TXN_OPTIONAL => 'O',
167             );
168              
169             # tx type
170             const TXT => (
171             TXT_EXPLICIT => 'explicit',
172             TXT_IMPLICIT => 'implicit',
173             );
174              
175             # object types that can appear in the payload
176             const OT => (
177             OT_LOCK => 'lock',
178             OT_TRANSACTION => 'transaction',
179             );
180              
181             # commands
182             const CMD => ();
183              
184             # stage return codes
185             const RC => (
186             RC_OK => 0,
187             RC_ERROR => 3,
188             RC_MANUAL => 7,
189             RC_INTERNAL_ERROR => 8,
190             );
191              
192             # ticket origins
193             const OR => (
194             OR_TEST => 'tst',
195             OR_SIF => 'sif',
196             );
197              
198             # ticket payload instruction commands
199             const IC => (
200             IC_ADD => 'add',
201             IC_UPDATE => 'update',
202             IC_DELETE => 'delete',
203             );
204              
205             # stages (see ticket stage value object)
206             const stages => (ST_TXSEL => 'txsel',);
207              
208             # stage position names
209             const stage_positions => (
210             STAGE_START => 'start',
211             STAGE_ACTIVE => 'active',
212             STAGE_END => 'end',
213             );
214              
215             # notify
216             const MSG => (
217             MSG_NOTOK => 'not OK',
218             MSG_OK => 'OK',
219             );
220              
221             # languages
222             const LANG => (
223             LANG_DE => 'de',
224             LANG_EN => 'en',
225             );
226              
227             # --------------------------------------------------------------------------
228             # Start of Class::Value::String handling
229             # --------------------------------------------------------------------------
230 1         62 use constant CHARSET_HANDLER_HASH =>
231 1     1   6 (_AUTO => 'Data::Conveyor::Charset::ASCII',);
  1         2  
232 1     1   6 use constant MAX_LENGTH_HASH => (_AUTO => 2000,);
  1         2  
  1         380  
233              
234             sub get_charset_handler_for {
235 0     0 1 0 my ($self, $object) = @_;
236 0         0 our %cache;
237 0         0 my $object_type =
238             Class::Scaffold::Factory::Type->get_factory_type_for($object);
239              
240             # cache the every_hash result for efficiency reasons
241 0 0       0 $cache{charset_handler_hash} = $self->every_hash('CHARSET_HANDLER_HASH')
242             unless defined $cache{charset_handler_hash};
243 0 0       0 return $cache{charset_handler_hash}{_AUTO} unless defined $object_type;
244 0   0     0 my $class = $cache{charset_handler_hash}{$object_type}
245             || $cache{charset_handler_hash}{_AUTO};
246              
247             # Cache the charset handler, because there should be only one per
248             # subclass. Note that this isn't the same as making
249             # Data::Conveyor::Charset::ViaHash a singleton, because there would then
250             # be only one in total. We want one per subclass.
251 0   0     0 $cache{charset_handler}{$class} ||= $class->new;
252             }
253              
254             sub get_max_length_for {
255 0     0 1 0 my ($self, $object) = @_;
256 0         0 our %cache;
257 0         0 my $object_type =
258             Class::Scaffold::Factory::Type->get_factory_type_for($object);
259              
260             # cache the every_hash result for efficiency reasons
261 0 0       0 $cache{max_length_hash} = $self->every_hash('MAX_LENGTH_HASH')
262             unless defined $cache{max_length_hash};
263 0 0       0 return $cache{max_length_hash}{_AUTO} unless defined $object_type;
264 0 0       0 return $cache{max_length}{$object_type}
265             if defined $cache{max_length}{$object_type};
266 0   0     0 $cache{max_length}{$object_type} = $cache{max_length_hash}{$object_type}
267             || $cache{max_length_hash}{_AUTO};
268             }
269              
270             sub setup {
271 1     1 1 80 my $self = shift;
272 1         12 $self->SUPER::setup(@_);
273 1         2091 require Class::Value::String;
274 1         1858 Class::Value::String->string_delegate($self);
275             }
276              
277             # --------------------------------------------------------------------------
278             # End of Class::Value::String handling
279             # --------------------------------------------------------------------------
280             # truth: how are boolean values represented in the storage? truth() uses these
281             # constants. Some systems might want 1 and 0 for these values.
282 1     1   5 use constant YES => 'Y';
  1         2  
  1         42  
283 1     1   5 use constant NO => 'N';
  1         5  
  1         49  
284              
285             # service interface parameters
286 1     1   4 use constant SIP_STRING => 'string';
  1         3  
  1         43  
287 1     1   4 use constant SIP_BOOLEAN => 'boolean';
  1         2  
  1         33  
288 1     1   5 use constant SIP_MANDATORY => 'mandatory';
  1         2  
  1         40  
289 1     1   5 use constant SIP_OPTIONAL => 'optional';
  1         2  
  1         254  
290              
291             sub FINAL_TICKET_STAGE {
292 0     0 1 0 my $self = shift;
293 0         0 $self->make_obj('value_ticket_stage')->new_end('ticket');
294             }
295              
296             # for display purposes
297             sub STAGE_ORDER {
298 0     0 1 0 local $_ = $_[0]->delegate;
299 0         0 ($_->ST_TXSEL, 'ticket',);
300             }
301              
302             # ----------------------------------------------------------------------
303             # class name-related code
304             sub STAGE_CLASS_NAME_HASH {
305 1     1 1 830 local $_ = $_[0]->delegate;
306 1         13 ($_->ST_TXSEL => 'Data::Conveyor::Stage::TxSelector',);
307             }
308             Class::Scaffold::Factory::Type->register_factory_type(
309             exception_container => 'Data::Conveyor::Exception::Container',
310             exception_handler => 'Data::Conveyor::Exception::Handler',
311             lock => 'Data::Conveyor::Ticket::Lock',
312             monitor => 'Data::Conveyor::Monitor',
313             mutex => 'Data::Conveyor::Mutex',
314             payload_common => 'Data::Conveyor::Ticket::Payload::Common',
315             payload_instruction_container =>
316             'Data::Conveyor::Ticket::Payload::Instruction::Container',
317             payload_instruction_factory =>
318             'Data::Conveyor::Ticket::Payload::Instruction::Factory',
319             payload_lock => 'Data::Conveyor::Ticket::Payload::Lock',
320             payload_transaction => 'Data::Conveyor::Ticket::Payload::Transaction',
321             service_interface_shell => 'Data::Conveyor::Service::Interface::Shell',
322             service_interface_soap => 'Data::Conveyor::Service::Interface::SOAP',
323             service_methods => 'Data::Conveyor::Service::Methods',
324             service_result_container => 'Data::Conveyor::Service::Result::Container',
325             service_result_scalar => 'Data::Conveyor::Service::Result::Scalar',
326             service_result_tabular => 'Data::Conveyor::Service::Result::Tabular',
327             template_factory => 'Data::Conveyor::Template::Factory',
328             test_ticket => 'Data::Conveyor::Test::Ticket',
329             ticket => 'Data::Conveyor::Ticket',
330             ticket_dispatcher => 'Data::Conveyor::Ticket::Dispatcher',
331             ticket_dispatcher_test => 'Data::Conveyor::Ticket::Dispatcher::Test',
332             ticket_facets => 'Data::Conveyor::Ticket::Facets',
333             ticket_payload => 'Data::Conveyor::Ticket::Payload',
334             test_util_loader => 'Data::Conveyor::Test::UtilLoader',
335             ticket_provider => 'Data::Conveyor::Ticket::Provider',
336             ticket_transition => 'Data::Conveyor::Ticket::Transition',
337             transaction => 'Data::Conveyor::Ticket::Transaction',
338             transaction_factory => 'Data::Conveyor::Transaction::Factory',
339             value_lock_type => 'Data::Conveyor::Value::LockType',
340             value_ticket_rc => 'Data::Conveyor::Value::Ticket::RC',
341             value_ticket_stage => 'Data::Conveyor::Value::Ticket::Stage',
342             value_ticket_status => 'Data::Conveyor::Value::Ticket::Status',
343             stage_delegate => 'Data::Conveyor::Delegate::Stage',
344             );
345 1         41 use constant DELEGATE_ACCESSORS => qw(
346             stage_delegate
347 1     1   4 );
  1         3  
348 1         62 use constant STORAGE_CLASS_NAME_HASH => (
349              
350             # storage names
351             STG_DC_NULL => 'Data::Conveyor::Storage::Null',
352 1     1   5 );
  1         1  
353 1         90 use constant INSTRUCTION_CLASS_BASE =>
354 1     1   6 'Data::Conveyor::Ticket::Payload::Instruction';
  1         2  
355              
356             # used to generate instruction classes, see init() above
357 1     1 1 990 sub INSTRUCTION_CLASS_LIST { () }
358              
359             # ----------------------------------------------------------------------
360             # storage-related code
361 1         96 use constant STORAGE_TYPE_HASH => (
362             mutex => MUTEX_STORAGE_TYPE,
363             ticket_transition => 'memory_storage',
364 1     1   5 );
  1         1  
365              
366             sub mutex_storage {
367 0     0 1 0 my $self = shift;
368 0   0     0 $self->storage_cache->{ MUTEX_STORAGE_TYPE() } ||=
369             $self->make_storage_object($self->mutex_storage_name,
370             $self->mutex_storage_args);
371             }
372              
373             # ----------------------------------------------------------------------
374             # how many transactions of a given object_type may occur in a ticket of a given
375             # ticket type?
376 1     1   6 use constant object_limit => {};
  1         2  
  1         345  
377              
378             sub get_object_limit {
379 0     0 1 0 my ($self, $ticket_type, $object_type) = @_;
380 0   0     0 my $limit = $self->object_limit->{$ticket_type}{$object_type}
381             || $self->default_object_limit;
382 0 0       0 return $limit if defined $limit;
383 0         0 throw Error::Hierarchy::Internal::CustomMessage(
384             custom_message => sprintf
385             "Can't determine object limit for ticket type [%s], object type [%s]",
386             $ticket_type, $object_type
387             );
388             }
389              
390             # ----------------------------------------------------------------------
391             # code to make objects of various types
392             sub make_stage_object {
393 2     2 1 14 my ($self, $stage_type, @args) = @_;
394 2         13 assert_defined $stage_type, 'called without stage type.';
395 2         37 my $class = $self->get_stage_class_name_for($stage_type);
396 2         55 assert_defined $class,
397             "no stage class name found for [$stage_type]. Hint: did you define it in STAGE_CLASS_NAME_HASH?";
398 1         14 load_class $class, $self->test_mode;
399 1         39 $class->new(@args);
400             }
401              
402             # like the generated make_*_object() methods, but cache the object.
403             sub make_ticket_transition_object {
404 0     0 1   my $self = shift;
405 0   0       our $ticket_transition_object ||= $self->make_obj(ticket_transition => @_);
406             }
407              
408             sub allowed_dispatcher_stages {
409 0     0 1   my $self = shift;
410 0           $self->delegate->stages;
411             }
412              
413             1;
414              
415              
416             __END__