view Lib/IMPL/Web/Application.pm @ 134:44977efed303

Significant performance optimizations Fixed recursion problems due converting objects to JSON Added cache support for the templates Added discovery feature for the web methods
author wizard
date Mon, 21 Jun 2010 02:39:53 +0400
parents 06a34c197b05
children c5bc900eefd3
line wrap: on
line source

package IMPL::Web::Application;
use strict;
use warnings;

use base qw(IMPL::Config IMPL::Object::Singleton);

require IMPL::Web::Application::Action;
require IMPL::Web::Application::Response;

use IMPL::Class::Property;
use CGI;

__PACKAGE__->PassThroughArgs;

BEGIN {
    public property handlerError => prop_all;
    public property actionFactory => prop_all;
    public property handlersQuery => prop_all | prop_list;
    public property responseCharset => prop_all;
    public property security => prop_all;
    public property options => prop_all;
    public property fetchRequestMethod => prop_all;
}

sub CTOR {
	my ($this) = @_;
	
	$this->actionFactory('IMPL::Web::Application::Action') unless $this->actionFactory;
	$this->responseCharset('utf-8') unless $this->responseCharset;
	$this->fetchRequestMethod(\&defaultFetchRequest) unless $this->fetchRequestMethod;
	$this->handlerError(\&defaultHandlerError) unless $this->handlerError;
}

sub Run {
    my ($this) = @_;
    
    while (my $query = $this->FetchRequest()) {
        
        my $action = $this->actionFactory->new(
        	query => $query,
        	application => $this, 
        );
        
        eval {
	        $action->response->charset($this->responseCharset);
	        
	        $action->ChainHandler($_) foreach $this->handlersQuery;
	        
	        $action->Invoke();
	        
	        $action->response->Complete;
        };
        if ($@) {
        	my $e = $@;
        	# we are expecting this method to be safe otherwise we can trust nothing in this wolrd 
        	$this->handlerError()->($this,$action,$e);
        }
    }
}

sub FetchRequest {
	my ($this) = @_;
	
	if( ref $this->fetchRequestMethod eq 'CODE' ) {
		return $this->fetchRequestMethod->($this);
	} else {
		die new IMPL::Exception("Unknown fetchRequestMethod type",ref $this->fetchRequestMethod);
	}
}

{
	my $hasFetched = 0;

	sub defaultFetchRequest {
		my ($this) = @_;
		return undef if $hasFetched;
		$hasFetched = 1;
		my $query = CGIWrapper->new();
		$query->charset($this->responseCharset);
		return $query;
	}
}

sub defaultHandlerError {
	my ($this,$action,$e) = @_;
	warn $e;
	if ( eval {	$action->ReinitResponse(); 1; } ) {
		$action->response->contentType('text/plain');
		$action->response->charset($this->responseCharset);
		$action->response->status(500);
		my $hout = $action->response->streamBody;
		print $hout $e;
		$action->response->Complete();
	}	
}

package CGIWrapper;
use base qw(CGI);

use Encode;

sub param {
	my $this = shift;
	
	if (wantarray) {
		my @result = $this->SUPER::param(@_);
		
		return map Encode::is_utf8($_) ? $_ : Encode::decode($this->charset,$_,Encode::LEAVE_SRC), @result;
	} else {
		my $result = $this->SUPER::param(@_);
		
		return Encode::is_utf8($result) ? $result : Encode::decode($this->charset,$result,Encode::LEAVE_SRC);
	}

}

1;

__END__

=pod

=head1 SYNOPSIS

=begin code

require MyApp;

my $instance = spawn MyApp('app.config');

$instance->Run();

=end code

=head1 DESCRIPTION

C< use base qw( IMPL::Config IMPL::Object::Singleton )>

Зкземпляр приложения содержит в себе глобальные настройки, реализует контроллер запросов,
в качестве источника запросов используется CGI или иной совместимый модуль.

Процесс обработки запроса состоит из следующих частей

=over

=item 1

Получение cgi запроса

=item 2

Создание объекта C<IMPL::Web::Application::Action>

=item 3

Формирование цепочки вызовов при помощи C<< IMPL::Web::Application::Action->ChainHandler >>

=item 4

Выполнение запроса C<< IMPL::Web::Application::Action->Invoke >>

=cut

Также приложение поддерживает отложенное создание объектов, которые по первому обращению
к свойствам. Это реализовано в базовом классе C< IMPL::Configuration >. Для настройки
активаторов можно использовать свойство C<options>, в которое должен быть помещен хеш
со ссылками на активаторы, см. пример ниже C<CONFIGURATION>. 

=head2 CONFIGURATION

Ниже приведен пример конфигурации приложения

=begin code xml

<?xml version="1.0" encoding="UTF-8"?>
<Application id='app' type="Test::Web::Application::Instance">
	
	<!-- Begin custom properties -->
	<name>Sample application</name>
	<dataSource type='IMPL::Config::Activator' id='ds'>
		<factory>IMPL::Object</factory>
		<parameters type='HASH'>
			<db>data</db>
			<user>nobody</user>
		</parameters>
	</dataSource>
	<securityMod type='IMPL::Config::Activator'>
		<factory>IMPL::Object</factory>
		<parameters type='HASH'>
			<ds refid='ds'/>
		</parameters>
	</securityMod>	
	<!-- End custom properties -->
	
	<!-- direct access to the activators -->
	<options type="HASH">
		<dataSource refid='ds'/>
	</options>
	
	<!-- Set default output encoding, can be changed due query handling -->
	<responseCharset>utf-8</responseCharset>
	
	<!-- Actions creation configuration -->
	<actionFactory type="IMPL::Object::Factory">
		
		<!-- Construct actions -->		
		<factory>IMPL::Web::Application::Action</factory>
		<parameters type='HASH'>
			
			<!-- with special responseFactory -->
			<responseFactory type='IMPL::Object::Factory'>
			
				<!-- Where resopnses have a special streamOut -->
				<factory>IMPL::Web::Application::Response</factory>
				<parameters type='HASH'>
				
					<!-- in memory dummy output instead of STDOUT -->
					<streamOut>memory</streamOut>
					
				</parameters>
			</responseFactory>
		</parameters>
	</actionFactory>
	
	<!-- Query processing chain -->
	<handlersQuery type="IMPL::Object::List">
		<item type="IMPL::Web::QueryHandler::PageFormat">
			<templatesCharset>cp1251</templatesCharset>
		</item>
	</handlersQuery>
</Application>

=end code xml

=head1 MEMBERS

=over

=item C<[get,set] handlerError>

Обработчик который будет вызван в случае возникновения необработанной ошибки
в процессе работы приложения. После чего приложение корректно завершается.

=item C<[get,set] actionFactory>

Фабрика объектов, которая используется приложением, для создания объектов
типа C<IMPL::Web::Application::Action> при обработки C<CGI> запросов.

=begin code

my $action = $this->actionFactory->new(
    query => $query,
    application => $this, 
);

=end code

=item C< [get,set] fetchRequestMethod >

Метод получения CGI запроса. Возвращает C<CGI> объект следующего запроса, если
запросов больше нет, то возвращает C<undef>. По-умолчанию использует C<defaultFetchRequest>.

Может быть как ссылкой на функцию, так и объектом типа C<IMPL::Web::Application::RequestFetcher>.

=item C< [get,set,list] handlersQuery >

Список обработчиков запросов, которые будут переданы созданному объекту-действию.

=item C< [get,set] responseCharset>

Кодировка ответа клиенту.

=item C< [get,set] security >

Объект C<IMPL::Web::Security>, для работы с инфраструктурой безопасности.

=item C< [get,set] options >

Обычно ссылка на хеш с настраиваемыми объектами, используется для возможности
програмной настройки активаторов, т.к. напрямую через свойства приложения получить
к ним доступ не получится.
 
=back

=cut