Objetivos

Referências complementares

Conceito

Um clock é um processo executado periodicamente de acordo com um agendamento realizado. Tipicamente um clock é utilizado para atividades de manutenção sobre o Portal.

Existe um processo central, que é executado de acordo com uma periodicidade definida nas configurações do Portal (30 segundos, por default) que é o responsável por verificar o agendamento dos demais clocks e dispará-los se já estiver na hora de serem executados. Esse processo é o PortalClock. Ou seja, cada execução do PortalClock é uma “janela” para execução dos demais, caso já tenha passado o intervalo definido para cada um desde a última execução. Sempre que o PortalClock identifica que um determinado clock deve ser disparado ele cria uma thread para essa execução. Sendo assim, cada clock roda em uma thread separada de modo que a execução de um não interfira na execução de outro e nem na execução da thread principal do Portal.

Para exemplificar, imaginemos o cenário de termos apenas dois clocks agendados além do processo que os dispara, com as seguintes configurações:

Processo
Periodicidade

PortalClock (dispara os demais)

1 minuto

Clock A

2 minutos

Clock B

5 minutos

Na linha do tempo temos, a cada minuto, a possibilidade de execução dos clocks, quando o PortalClock verifica quando foi a última execução de cada clock configurado e analisa se já está na hora de ser executado novamente. Dessa forma, a linha do tempo com os clocks executados em cada janela de 1 minuto durante os 12 primeiros minutos ficaria da seguinte forma:

    0            1           2           3           4          5           6           7           8          9          10         11        12

    |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|

A e B         -            A           -           A          B           A           -          A           -        A e B        -           A

Repare que no instante 0 (ou seja, a primeira execução do PortalClock) os dois clocks são executados pois ainda não haviam sido executados nenhuma vez antes. A partir daí começa a valer o intervalo definido e o clock A é executado a cada dois minutos enquanto o clock B a cada 5 minutos. No entanto, em várias janelas definidas nenhum clock é de fato chamado pelo PortalClock pois ainda não passou o tempo definido de intervalo desde a última execução.

O Lumis utiliza esse mecanismo para, através de alguns clocks padrão existentes no produto, executar tarefas de manutenção periódicas. Um exemplo disso é o ContentClock, responsável, entre outras coisas, por mudar o status de um conteúdo de não publicado para publicado quando atingirmos a data de publicação definida para esse conteúdo.

Além disso, é possível criar clocks customizados e configurá-los em serviços para que sejam executados com a periodicidade desejada. A seguir veremos como criar clocks customizados.

Clocks customizados

Criando a classe que representa o clock

Uma classe de clock deve necessariamente implementar a interface IServiceClock. Essa interface possui apenas dois métodos: getNextScheduleTime, utilizado para identificar a data e hora da próxima execução e doTick, onde deve ser implementada a tarefa periódica, chamado quando o PortalClock identifica que o clock deve ser executado.

Uma boa opção para a criação de um clock, ao invés de implementar diretamente a interface IServiceClock é estender a classe GenericServiceClock classe também implementa IServiceClock e já implementa o método getNextScheduleTime com a inteligência de calcular a próxima execução com base nas configurações realizadas sobre o clock. Essas configurações são representadas pelo objeto ClockConfig é passado como parâmetro para esse método quando ele é chamado.

Uma questão importante a respeito da execução dos clocks, é sobre o contexto em que isso ocorre. Como, durante a execução de um clock, não estamos em uma chamada HTTP e muito menos em um contexto de ciclo de vida de uma interface DOUI, não temos os objetos request e response e tampouco o objeto douiContext, de onde normalmente são extraídas as informações quando estamos tratando da renderização de uma interface.

No caso de clocks, todo o contexto disponível está no objeto ClockConfig, passado como parâmetro para o método doTick e se resume ao conjunto de configurações realizadas para o clock no serviço ao qual ele pertence.

Além desse conjunto de configurações o método doTick também recebe um objeto sessionConfig com uma sessão criada através de impersonificação para o usuário FrameworkClockUser. Esse é um usuário de sistema do Portal, que possui as permissões necessárias para execução de tarefas agendadas. Com esse objeto o clock pode chamar API’s que precisem de validação de acesso sem possuir uma sessão que seria criada a partir do login de algum usuário, já que no contexto de execução de um clock não temos usuário logado.

A seguir vemos um exemplo de implementação do método doTick em uma classe de clock que estende GenericServiceClock. Nesse exemplo o objetivo do clock é realizar o arquivamento de todos os conteúdos mais antigos do que um determinado período, por exemplo, 30 dias.

publicvoid doTick(SessionConfig sessionConfig, ClockConfig clockConfig) 
                                  throws ServiceException, PortalException
{
       // Utiliza objeto para logar o resultado do clock em um log específico
       ILogger logger = LoggerFactory.getServiceLogger("arquivamentoNoticias");
       
       // Cria uma nova transação
       ITransaction transaction = PortalTransactionFactory.createTransaction();
       
       try
       {
             // Inicializa a transação
             transaction.begin();
             
             // Chama o método para realizar o arquivamento dos conteúdos antigos
             int numConteudosArquivados = arquivaConteudos(sessionConfig, transaction);
             
             // Registra o resultado da execução do clock no log
             logger.info("\n OK. " + numConteudosArquivados + " conteúdos arquivados.\n");
             
             // Faz o commit da transação após o arquivamento dos conteúdos
             transaction.commit();
       }
       catch (PortalException e) 
       {
             // Desfaz as alterações em caso de erro
             transaction.rollback();
             throwe;
       }
       finally
       {
             // Garante a finalização da transação criada
             transaction.dispose();
       }
 
}

Algumas observações são importantes sobre o exemplo acima. Em primeiro lugar, diferente de quando estamos no fluxo de renderização de uma interface DOUI e sempre temos em mãos uma transação que foi criada pelo próprio framework, em um clock a classe que implementa o clock é responsável por criar e finalizar a transação, quando ela for necessária. Nesse caso é fundamental que se tenha o cuidado de sempre realizar o dispose da transação dentro de um finally para garantir que ela será finalizada mesmo no caso de ocorrer alguma exceção.

Além disso, vemos que foi utilizado um logger;específico desse clock para indicar o resultado da limpeza a cada execução. A utilização de log em um clock é uma prática recomendada, pois facilita a validação do resultado da execução desse clock, e a análise de eventuais problemas.

Configurando um clock em um serviço

Um clock sempre pertence a um serviço. Assim, para que um clock seja registrado no Portal e passe a ser executado conforme o agendamento definido o serviço precisa ser registrado.

Para os casos em que o clock é genérico e não faz sentido configurá-lo em nenhum serviço previamente existente é possível criar um serviço apenas para o clock, não possuindo nenhuma interface. Nesse caso o ideal é marcar o serviço como não instanciável, utilizando o atributo isInstantiable="false"na tag service do arquivo servicedefinition.xml. Nesse caso, depois de registrado, o serviço não aparecerá na lista de serviços disponíveis para instanciar em um canal, mas o clock será executado conforme agendado.

A configuração de clocks em um serviço, inclusive a definição da classe que implementa o clock e as informações relativas a periodicidade, é realizada na tag clocks;dentro da tag service. Um serviço pode possuir diversos clocks configurados, basta representar cada um deles em uma tag clock;abaixo da tag clocks. A seguir vemos um exemplo de arquivo de definição de um serviço não instanciável que não possui interfaces, apenas um clock configurado para ser executado a cada seis horas.

<?xml version="1.0" encoding="UTF-8"?>
<serviceDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.lumis.com.br/lumisportal/xsd/servicedefinition.xsd">
       <service id="exemplo.clock" name="Exemplo de Clock" isInstantiable="false" type="lum_service">
             <description>Exemplo para a configuração de clock</description>
             <clocks>
                    <clock className="exemplo.clock.ClockExemplo" id="ClockExemplo">
                           <tickInterval type="hours">6</tickInterval>
                    </clock>
             </clocks>
       </service>
</serviceDefinition>

Na tag clock, são definidos através de atributos um id para o clock (o id “final” do clock para o Portal na verdade será o id do serviço + “.” + id do clock) e o nome da classe implementada para o clock, no atributo className.

As configurações de agendamento de execução, todas representadas por tags dentro da tag clock, são as seguintes:

No exemplo anterior, em que temos um processo que deve arquivar os conteúdos mais antigos do que 30 dias, gostaríamos que o processo fosse executado apenas uma vez por dia em um horário de baixo acesso, por exemplo às 03h da manhã. Além disso gostaríamos de definir um tempo limite de execução de 10 minutos. Nesse caso a configuração do clock ficaria da seguinte forma:

<clock className="exemplo.clock.ArquivamentoConteudosClock" id="arquivamentoClock">
       <tickInterval type="hours">24</tickInterval>
       <maxRunTime>600</maxRunTime>
       <startTime>03:00:00</startTime>
</clock>

Configurações globais

O clock do Portal pode ser habilitado ou desabilitado globalmente, assim como sua periodicidade pode ser definida no arquivo lumisportalconfig.xml. Veja abaixo o trecho que define essas configurações:
<portalClock>
       <enable>1</enable>
       <tickInterval>30&llt;/tickInterval>
</portalClock>

Na tag enable o clock pode ser habilitado ou desabilitado (por padrão ele está habilitado). Caso ele esteja desabilitado, nenhum clock de nenhum serviço será chamado pelo PortalClock.

A periodicidade definida em tickInterval (em segundos), define o tempo entre uma execução e outra do PortalClock, ou seja, define o tempo entre uma janela e outra de execução para chamada a todos os clocks configurados no Portal. Sendo assim, não faz sentido que o intervalo de execução de nenhum clock do Portal seja menor do que a periodicidade aqui definida, já que esses clocks só terão a oportunidade de execução nas janelas definidas por essa periodicidade global.

Outra característica implícita do tempo aqui definido é que ele acaba sendo como uma margem de erro para o horário definido para execução de clocks. Por exemplo, se temos aqui definida uma periodicidade de 2 minutos, um clock agendado para executar às 03h da manhã poderá ser executado entre 03h00 e 03h02.

Clocks padrão do Portal

Todos os serviços do tipo lum_content do Portal, por default já vem com um clock configurado caso não sejam especificados clocks na sua definição de serviços. O clock em questão é o ContentClock, que tem o objetivo de tratar a publicação e expiração agendada de conteúdos.

O ContentClock verifica todos os conteúdos que não estejam publicados e que tenham tido uma data de publicação configurada. Caso já tenhamos atingido a data de publicação, ele modifica o status desses conteúdos para “publicado”. Da mesma forma, ele verifica os conteúdos que estejam publicados e possuam uma data de expiração. Caso já tenhamos chegado à data de expiração, ele altera o status dos conteúdos para “não publicado”. Além disso, esse clock também remove o a indicação de conteúdo em destaque para aqueles que tenham uma data de expiração de destaque e essa data tenha passado. Além disso, esse clock cuida para que sejam disparados os eventos necessários para notificar o Portal sobre essas alterações de status, de modo que sejam realizadas as tarefas de limpeza de cache necessárias para que as interfaces reflitam essas mudanças.

Assim como acontece para outras configurações dos serviços, o ContentClock é automaticamente aplicado para todos os serviços do tipo lum_content caso não seja especificada a tag clocks na definição do serviço. Caso essa tag seja definida para que seja configurado um clock específico do serviço, por exemplo, passa a valer a configuração que está no arquivo de definição. Nesse caso, se for desejável que o ContentClock esteja ativo para o serviço em questão ele também precisa ser definido explicitamente na lista de clocks do serviço. Uma alternativa a essa abordagem é já implementar o clock customizado estendendo da classe ContentClock e garantir que no método doTick toda implementação da super classe será chamada.

Alguns serviços padrão utilizam clocks específicos além dos diversos serviços que possuem o ContentClock. Esses clocks específicos estão brevemente descritos a seguir:

Monitoramento da execução de clocks

Na área administrativa do Portal, dentro de Módulos -> Tarefas Agendadas, é possível administrar todos os clocks que temos configurados para execução. Abaixo uma representação da interface Gerenciador de Tarefas Agendadas:

image001.jpg

Nessa interface é possível habilitar e desabilitar a execução dos clocks de forma pontual, além de reiniciar o estado de execução de um clock marcando-o como não executando, através do botão Resetar.

É importante ressaltar que a ação de resetar uma tarefa não interrompe a execução da thread, caso ela esteja em andamento. Assim, essa ação pode ocasionar situações imprevistas. Por exemplo no caso de um thread estar em execução, a ação de resetar pode fazer com que outra thread seja disparada e tenhamos duas execuções simultâneas. O ideal é que esse botão seja utilizado apenas para limpar o estado de execução de tarefas nos casos em que se tenha certeza de que a thread não está mais em execução, por exemplo após um reinício forçado do servidor.

As informações existentes nessa interface para cada clock configurado são as seguintes:

Existe uma particularidade na exibição do campo “Última Execução”. Além da data, esse campo exibe a hora de início da última execução seguida da hora de término da última execução, no formato HH:MM:SS – HH:MM:SS. A exceção é no caso da execução ter ocorrido toda dentro de um mesmo segundo. Nesse caso, para simplificar a visualização, é exibido apenas um horário, também no formato HH:MM:SS.

A partir das informações disponíveis no Gerenciador de Tarefas Agendadas é possível realizar análises sobre problemas na execução de clocks, como por exemplo confirmar se um determinado clock está sendo executado ou saber se sua última execução foi no momento em que se esperaria que fosse.

Autor: Sergio Fernandes