1# -- 2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/ 3# -- 4# This software comes with ABSOLUTELY NO WARRANTY. For details, see 5# the enclosed file COPYING for license information (GPL). If you 6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. 7# -- 8 9package Kernel::System::ObjectManager; 10## nofilter(TidyAll::Plugin::OTRS::Perl::LayoutObject) 11## nofilter(TidyAll::Plugin::OTRS::Perl::PodSpelling) 12## nofilter(TidyAll::Plugin::OTRS::Perl::Require) 13## nofilter(TidyAll::Plugin::OTRS::Perl::SyntaxCheck) 14 15use strict; 16use warnings; 17 18use Carp (); 19use Scalar::Util qw(weaken); 20 21# use the "standard" modules directly, so that persistent environments 22# like mod_perl and FastCGI pre-load them at startup 23 24use Kernel::Config; 25use Kernel::Output::HTML::Layout; 26use Kernel::System::Auth; 27use Kernel::System::AuthSession; 28use Kernel::System::Cache; 29use Kernel::System::DateTime; 30use Kernel::System::DB; 31use Kernel::System::Encode; 32use Kernel::System::Group; 33use Kernel::System::Log; 34use Kernel::System::Main; 35use Kernel::System::Web::Request; 36use Kernel::System::User; 37 38=head1 NAME 39 40Kernel::System::ObjectManager - Central singleton manager and object instance generator 41 42=head1 SYNOPSIS 43 44 # In top level scripts only! 45 local $Kernel::OM = Kernel::System::ObjectManager->new(); 46 47 # Everywhere: get a singleton instance (and create it, if needed). 48 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 49 50 # Remove singleton objects and all their dependencies. 51 $Kernel::OM->ObjectsDiscard( 52 Objects => ['Kernel::System::Ticket', 'Kernel::System::Queue'], 53 ); 54 55=head1 DESCRIPTION 56 57The ObjectManager is the central place to create and access singleton OTRS objects (via C<L</Get()>>) 58as well as create regular (unmanaged) object instances (via C<L</Create()>>). 59 60=head2 How does singleton management work? 61 62It creates objects as late as possible and keeps references to them. Upon destruction the objects 63are destroyed in the correct order, based on their dependencies (see below). 64 65=head2 How to use it? 66 67The ObjectManager must always be provided to OTRS by the top level script like this: 68 69 use Kernel::System::ObjectManager; 70 local $Kernel::OM = Kernel::System::ObjectManager->new( 71 # possible options for module constructors here 72 LogObject { 73 LogPrefix => 'OTRS-MyTestScript', 74 }, 75 ); 76 77Then in the code any singleton object can be retrieved that the ObjectManager can handle, 78like Kernel::System::DB: 79 80 return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare('SELECT 1'); 81 82=head2 Which objects can be loaded? 83 84The ObjectManager can load every object that declares its dependencies like this in the Perl package: 85 86 package Kernel::System::Valid; 87 88 use strict; 89 use warnings; 90 91 our @ObjectDependencies = ( 92 'Kernel::System::Cache', 93 'Kernel::System::DB', 94 'Kernel::System::Log', 95 ); 96 97The C<@ObjectDependencies> is the list of objects that the current object will depend on. They will 98be destroyed only after this object is destroyed (only for singletons). 99 100If you want to signal that a package can NOT be loaded by the ObjectManager, you can use the 101C<$ObjectManagerDisabled> flag: 102 103 package Kernel::System::MyBaseClass; 104 105 use strict; 106 use warnings; 107 108 our $ObjectManagerDisabled = 1; 109 110There are a few flags available to convey meta data about the packages to the object manager. 111 112To indicate that a certain package can B<only> be loaded as a singleton, you can use the 113C<IsSingleton> flag. Similarly, you can indicate that a certain package can B<only> be 114created as unmanaged instance, and B<not> as a singleton via the C<NonSingleton> flag. 115By default, the ObjectManager will die if a constructor does not return an object. 116To suppress this in the C<L</Create()>> method, you can use the C<AllowConstructorFailure> 117flag (this will not work with C<L</Get()>>). 118 119 package Kernel::System::MyPackage; 120 121 use strict; 122 use warnings; 123 124 our %ObjectManagerFlags = ( 125 IsSingleton => 1, # default 0 126 NonSingleton => 0, # default 0 127 AllowConstructorFailure => 0, # default 0 128 ); 129 130=head1 PUBLIC INTERFACE 131 132=head2 new() 133 134Creates a new instance of Kernel::System::ObjectManager. 135 136This is typically B<only> needed in top level (C<bin/>) scripts! All parts of the OTRS API assume 137the ObjectManager to be present in C<$Kernel::OM> and use it. 138 139Sometimes objects need parameters to be sent to their constructors, 140these can also be passed to the ObjectManager's constructor like in the following example. 141The hash reference will be flattened and passed to the constructor of the object(s). 142 143 local $Kernel::OM = Kernel::System::ObjectManager->new( 144 Kernel::System::Log => { 145 LogPrefix => 'OTRS-MyTestScript', 146 }, 147 ); 148 149Alternatively, C<L</ObjectParamAdd()>> can be used to set these parameters at runtime (but this 150must happen before the object was created). 151 152If the C<< Debug => 1 >> option is present, destruction of objects 153is checked, and a warning is emitted if objects persist after the 154attempt to destroy them. 155 156=cut 157 158sub new { 159 my ( $Type, %Param ) = @_; 160 my $Self = bless {}, $Type; 161 162 $Self->{Debug} = delete $Param{Debug}; 163 164 for my $Parameter ( sort keys %Param ) { 165 $Self->{Param}->{$Parameter} = $Param{$Parameter}; 166 } 167 168 # Kernel::System::Encode->new() initializes the environment, so we need to 169 # already create an instance here to make sure it is always done and done 170 # at the beginning of things. 171 $Self->Get('Kernel::System::Encode'); 172 173 return $Self; 174} 175 176=head2 Get() 177 178Retrieves a singleton object, and if it not yet exists, implicitly creates one for you. 179 180 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 181 182 # On the second call, this returns the same ConfigObject as above. 183 my $ConfigObject2 = $Kernel::OM->Get('Kernel::Config'); 184 185=cut 186 187sub Get { ## no critic 188 189 # No param unpacking for increased performance 190 if ( $_[1] && $_[0]->{Objects}->{ $_[1] } ) { 191 return $_[0]->{Objects}->{ $_[1] }; 192 } 193 194 if ( !$_[1] ) { 195 $_[0]->_DieWithError( 196 Error => "Error: Missing parameter (object name)", 197 ); 198 } 199 200 return $_[0]->_ObjectBuild( Package => $_[1] ); 201} 202 203=head2 Create() 204 205Creates a new object instance. This instance will not be managed by the object manager later on. 206 207 my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 208 209 # On the second call, this creates a new independent instance. 210 my $DateTimeObject2 = $Kernel::OM->Create('Kernel::System::DateTime'); 211 212It is also possible to pass in constructor parameters: 213 214 my $DateTimeObject = $Kernel::OM->Create( 215 'Kernel::System::DateTime', 216 ObjectParams => { 217 Param1 => 'Value1', 218 }, 219 ); 220 221By default, this method will C<die>, if the package cannot be instantiated or the constructor returns undef. 222You can suppress this with C<< Silent => 1 >>, for example to not cause exceptions when trying 223to load modules based on user configuration. 224 225 my $CustomObject = $Kernel::OM->Create( 226 'Kernel::System::CustomObject', 227 Silent => 1, 228 ); 229 230=cut 231 232sub Create { 233 my ( $Self, $Package, %Param ) = @_; 234 235 if ( !$Package ) { 236 $Self->_DieWithError( 237 Error => "Error: Missing parameter (object name)", 238 ); 239 } 240 241 return $Self->_ObjectBuild( 242 %Param, 243 Package => $Package, 244 NoSingleton => 1, 245 ); 246} 247 248sub _ObjectBuild { 249 my ( $Self, %Param ) = @_; 250 251 my $Package = $Param{Package}; 252 eval { 253 my $FileName = $Param{Package} =~ s{::}{/}smxgr; 254 require $FileName . '.pm'; 255 }; 256 if ($@) { 257 if ( $Param{Silent} ) { 258 return; # don't throw 259 } 260 $Self->_DieWithError( 261 Error => "$Package could not be loaded: $@", 262 ); 263 } 264 265 # Kernel::Config does not declare its dependencies (they would have to be in 266 # Kernel::Config::Defaults), so assume [] in this case. 267 my $Dependencies = []; 268 269 no strict 'refs'; ## no critic 270 my %ObjectManagerFlags = %{ $Package . '::ObjectManagerFlags' }; 271 use strict 'refs'; 272 273 if ( $Package ne 'Kernel::Config' ) { 274 no strict 'refs'; ## no critic 275 if ( !exists ${ $Package . '::' }{ObjectDependencies} ) { 276 $Self->_DieWithError( Error => "$Package does not declare its object dependencies!" ); 277 } 278 $Dependencies = \@{ $Package . '::ObjectDependencies' }; 279 280 if ( ${ $Package . '::ObjectManagerDisabled' } ) { 281 $Self->_DieWithError( Error => "$Package cannot be loaded via ObjectManager!" ); 282 } 283 284 if ( $Param{NoSingleton} ) { 285 if ( $ObjectManagerFlags{IsSingleton} ) { 286 $Self->_DieWithError( 287 Error => 288 "$Package cannot be created as a new instance via ObjectManager! Use Get() instead of Create() to fetch the singleton." 289 ); 290 } 291 } 292 else { 293 if ( $ObjectManagerFlags{NonSingleton} ) { 294 $Self->_DieWithError( 295 Error => 296 "$Package cannot be loaded as a singleton via ObjectManager! Use Create() instead of Get() to create new instances." 297 ); 298 } 299 } 300 301 use strict 'refs'; 302 } 303 $Self->{ObjectDependencies}->{$Package} = $Dependencies; 304 305 my $NewObject = $Package->new( 306 %{ $Param{ObjectParams} // $Self->{Param}->{$Package} // {} } 307 ); 308 309 if ( !defined $NewObject ) { 310 if ( $Param{Silent} || $ObjectManagerFlags{AllowConstructorFailure} ) { 311 return; # don't throw 312 } 313 $Self->_DieWithError( 314 Error => "The constructor of $Package returned undef.", 315 ); 316 } 317 318 return $NewObject if ( $Param{NoSingleton} ); 319 320 $Self->{Objects}->{$Package} = $NewObject; 321 322 return $NewObject; 323} 324 325=head2 ObjectInstanceRegister() 326 327Adds an existing object instance to the ObjectManager so that it can be accessed by other objects. 328 329This should B<only> be used on special circumstances, e. g. in the unit tests to pass C<$Self> to the 330ObjectManager so that it is also available from there as 'Kernel::System::UnitTest'. 331 332 $Kernel::OM->ObjectInstanceRegister( 333 Package => 'Kernel::System::UnitTest', 334 Object => $UnitTestObject, 335 Dependencies => [], # optional, specify OM-managed packages that the object might depend on 336 ); 337 338=cut 339 340sub ObjectInstanceRegister { 341 my ( $Self, %Param ) = @_; 342 343 if ( !$Param{Package} || !$Param{Object} ) { 344 $Self->_DieWithError( Error => 'Need Package and Object.' ); 345 } 346 347 if ( defined $Self->{Objects}->{ $Param{Package} } ) { 348 $Self->_DieWithError( Error => 'Need $Param{Package} is already registered.' ); 349 } 350 351 $Self->{Objects}->{ $Param{Package} } = $Param{Object}; 352 $Self->{ObjectDependencies}->{ $Param{Package} } = $Param{Dependencies} // []; 353 354 return 1; 355} 356 357=head2 ObjectParamAdd() 358 359Adds arguments that will be passed to constructors of classes 360when they are created, in the same format as the C<L<new()>> method 361receives them. 362 363 $Kernel::OM->ObjectParamAdd( 364 'Kernel::System::Ticket' => { 365 Key => 'Value', 366 }, 367 ); 368 369To remove a key again, send undef as a value: 370 371 $Kernel::OM->ObjectParamAdd( 372 'Kernel::System::Ticket' => { 373 Key => undef, # this will remove the key from the hash 374 }, 375 ); 376 377=cut 378 379sub ObjectParamAdd { 380 my ( $Self, %Param ) = @_; 381 382 for my $Package ( sort keys %Param ) { 383 if ( ref( $Param{$Package} ) eq 'HASH' ) { 384 for my $Key ( sort keys %{ $Param{$Package} } ) { 385 if ( defined $Key ) { 386 $Self->{Param}->{$Package}->{$Key} = $Param{$Package}->{$Key}; 387 } 388 else { 389 delete $Self->{Param}->{$Package}->{$Key}; 390 } 391 } 392 } 393 else { 394 $Self->{Param}->{$Package} = $Param{$Package}; 395 } 396 } 397 return; 398} 399 400=head2 ObjectEventsHandle() 401 402Execute all queued (C<< Transaction => 1 >>) events for all singleton objects 403that the ObjectManager created before. This can be used to flush the event queue 404before destruction, for example. 405 406 $Kernel::OM->ObjectEventsHandle(); 407 408=cut 409 410sub ObjectEventsHandle { 411 my ( $Self, %Param ) = @_; 412 413 my $HasQueuedTransactions; 414 EVENTS: 415 for my $Counter ( 1 .. 10 ) { 416 $HasQueuedTransactions = 0; 417 EVENTHANDLERS: 418 for my $EventHandler ( @{ $Self->{EventHandlers} } ) { 419 420 # since the event handlers are weak references, 421 # they might be undef by now. 422 next EVENTHANDLERS if !defined $EventHandler; 423 if ( $EventHandler->EventHandlerHasQueuedTransactions() ) { 424 $HasQueuedTransactions = 1; 425 $EventHandler->EventHandlerTransaction(); 426 } 427 } 428 if ( !$HasQueuedTransactions ) { 429 last EVENTS; 430 } 431 } 432 if ($HasQueuedTransactions) { 433 warn "Unable to handle all pending events in 10 iterations"; 434 } 435 delete $Self->{EventHandlers}; 436 437 return; 438} 439 440=head2 ObjectsDiscard() 441 442Discards internally stored objects, so that the next access to objects 443creates them newly. If a list of object names is passed, only 444the supplied objects and their recursive dependencies are destroyed. 445If no list of object names is passed, all stored objects are destroyed. 446 447 $Kernel::OM->ObjectsDiscard(); 448 449 $Kernel::OM->ObjectsDiscard( 450 Objects => ['Kernel::System::Ticket', 'Kernel::System::Queue'], 451 452 # optional 453 # forces the packages to be reloaded from the file system 454 # sometimes necessary with mod_perl when running CodeUpgrade during a package upgrade 455 # if no list of object names is passed, all stored objects are destroyed 456 # and forced to be reloaded 457 ForcePackageReload => 1, 458 ); 459 460Mostly used for tests that rely on fresh objects, or to avoid large 461memory consumption in long-running processes. 462 463Note that if you pass a list of objects to be destroyed, they are destroyed 464in in the order they were passed; otherwise they are destroyed in reverse 465dependency order. 466 467=cut 468 469sub ObjectsDiscard { 470 my ( $Self, %Param ) = @_; 471 472 # fire outstanding events before destroying anything 473 $Self->ObjectEventsHandle(); 474 475 # destroy objects before their dependencies are destroyed 476 477 # first step: get the dependencies into a single hash, 478 # so that the topological sorting goes faster 479 my %ReverseDependencies; 480 my @AllObjects; 481 for my $Object ( sort keys %{ $Self->{Objects} } ) { 482 my $Dependencies = $Self->{ObjectDependencies}->{$Object}; 483 484 for my $Dependency (@$Dependencies) { 485 486 # undef happens to be the value that uses the least amount 487 # of memory in Perl, and we are only interested in the keys 488 $ReverseDependencies{$Dependency}->{$Object} = undef; 489 } 490 push @AllObjects, $Object; 491 } 492 493 # During an OTRS package upgrade the packagesetup code module has just 494 # recently been copied to it's location in the file system. 495 # In a persistent Perl environment an old version of the module might still be loaded, 496 # as watchdogs like Apache2::Reload haven't had a chance to reload it. 497 # So we need to make sure that the new version is being loaded. 498 # Kernel::System::Main::Require() checks the relative file path, so we need to remove that from %INC. 499 # This is only needed in persistent Perl environment, but does no harm in a CGI environment. 500 if ( $Param{ForcePackageReload} ) { 501 502 my @Objects; 503 if ( $Param{Objects} && @{ $Param{Objects} } ) { 504 @Objects = @{ $Param{Objects} }; 505 } 506 else { 507 @Objects = @AllObjects; 508 } 509 510 for my $Object (@Objects) { 511 512 # convert :: to / in order to build a file system path name 513 my $ObjectPath = $Object; 514 $ObjectPath =~ s/::/\//g; 515 516 # attach .pm as file extension 517 $ObjectPath .= '.pm'; 518 519 # delete from global %INC hash 520 delete $INC{$ObjectPath}; 521 } 522 } 523 524 # second step: post-order recursive traversal 525 my %Seen; 526 my @OrderedObjects; 527 my $Traverser; 528 $Traverser = sub { 529 my ($Object) = @_; 530 return if $Seen{$Object}++; 531 for my $ReverseDependency ( sort keys %{ $ReverseDependencies{$Object} } ) { 532 $Traverser->($ReverseDependency); 533 } 534 push @OrderedObjects, $Object; 535 }; 536 537 if ( $Param{Objects} ) { 538 for my $Object ( @{ $Param{Objects} } ) { 539 $Traverser->($Object); 540 } 541 } 542 else { 543 for my $Object (@AllObjects) { 544 $Traverser->($Object); 545 } 546 } 547 undef $Traverser; 548 549 # third step: destruction 550 if ( $Self->{Debug} ) { 551 552 # If there are undeclared dependencies between objects, destruction 553 # might not work in the order that we calculated, but might still work 554 # out in the end. 555 my %DestructionFailed; 556 for my $Object (@OrderedObjects) { 557 my $Checker = $Self->{Objects}->{$Object}; 558 weaken($Checker); 559 delete $Self->{Objects}->{$Object}; 560 561 if ( defined $Checker ) { 562 $DestructionFailed{$Object} = $Checker; 563 weaken( $DestructionFailed{$Object} ); 564 } 565 } 566 for my $Object ( sort keys %DestructionFailed ) { 567 if ( defined $DestructionFailed{$Object} ) { 568 warn "DESTRUCTION OF $Object FAILED!\n"; 569 if ( eval { require Devel::Cycle; 1 } ) { 570 Devel::Cycle::find_cycle( $DestructionFailed{$Object} ); 571 } 572 else { 573 warn "To get more debugging information, please install Devel::Cycle."; 574 } 575 } 576 } 577 } 578 else { 579 for my $Object (@OrderedObjects) { 580 delete $Self->{Objects}{$Object}; 581 } 582 } 583 584 # if an object requests an already destroyed object 585 # in its DESTROY method, we might hold it again, and must try again 586 # (but not infinitely) 587 if ( !$Param{Objects} && keys %{ $Self->{Objects} } ) { 588 if ( $Self->{DestroyAttempts} && $Self->{DestroyAttempts} > 3 ) { 589 $Self->_DieWithError( Error => "Loop while destroying objects!" ); 590 } 591 592 $Self->{DestroyAttempts}++; 593 $Self->ObjectsDiscard(); 594 $Self->{DestroyAttempts}--; 595 } 596 597 return 1; 598} 599 600=head2 ObjectRegisterEventHandler() 601 602Registers an object that can handle asynchronous events. 603 604 $Kernel::OM->ObjectRegisterEventHandler( 605 EventHandler => $EventHandlerObject, 606 ); 607 608The C<EventHandler> object should inherit from L<Kernel::System::EventHandler>. 609The object manager will call that object's C<EventHandlerHasQueuedTransactions> 610method, and if that returns a true value, calls its C<EventHandlerTransaction> method. 611 612=cut 613 614sub ObjectRegisterEventHandler { 615 my ( $Self, %Param ) = @_; 616 if ( !$Param{EventHandler} ) { 617 die "Missing parameter EventHandler"; 618 } 619 push @{ $Self->{EventHandlers} }, $Param{EventHandler}; 620 weaken( $Self->{EventHandlers}[-1] ); 621 return 1; 622} 623 624sub _DieWithError { 625 my ( $Self, %Param ) = @_; 626 627 if ( $Self->{Objects}->{'Kernel::System::Log'} ) { 628 $Self->{Objects}->{'Kernel::System::Log'}->Log( 629 Priority => 'Error', 630 Message => $Param{Error}, 631 ); 632 } 633 634 Carp::croak $Param{Error}; # This will die(). 635} 636 637sub DESTROY { 638 my ($Self) = @_; 639 640 # Make sure $Kernel::OM is still available in the destructor 641 local $Kernel::OM = $Self; 642 $Self->ObjectsDiscard(); 643 644 return; 645} 646 647=head1 TERMS AND CONDITIONS 648 649This software is part of the OTRS project (L<https://otrs.org/>). 650 651This software comes with ABSOLUTELY NO WARRANTY. For details, see 652the enclosed file COPYING for license information (GPL). If you 653did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 654 655=cut 656 6571; 658