JMX e Quartz com Spring no ORACLE Weblogic 10.3

Java Management Extensions (JMX) é uma tecnologia Java que permite monitorar e gerenciar aplicações Java. É muito comum utilizar este recurso em soluções corporativas escritas em Java. Neste post vou mostrar como expor um bean do Spring como um MBean do JMX.

Vamos criar um job no Quartz que de tempos em tempos chama um bean do Spring, com os recursos de JMX do Spring vamos parar este serviço no Quartz de fora da aplicação. Esta aplicação vai estar rodando no ORACLE Weblogic 10.3 e o deploy sera realizado através do maven 2.



Um pouco mais sobre JMX

JMX é baseado em uma arquitetura de 3 camadas. As camadas são as seguintes:
  • Probe Level:Nesta camada ficam os famosos MBean. Esta camada também é conhecida como camada de instrumentalização. Mais a frente vamos ver como fazer isto com Spring!
  • Agent Level:Nesta camada reside o MBeanServer, que fica entre os MBeans e o nível de gerenciamento. O Spring também facilidades para o MBeanServer.
  • Remote Management Level: Por fim o nível de gerenciamento, este nível permite que aplicações acessem o servidor de MBeans através de conectores e adaptadores. Os conectores permitem acesso via RMI, WS e JMS em quanto os adaptadores permitem acesso HTTP e SNMP por exemplo.
Arquitetura de 3 camadas do JMX


JMX é suportado por todos os principais container do mercado. Existe um console genérico chamado de JConsole que vem na pasta bin do JDK do java, mais adiante neste post vou mostrar como utilizar o JConsole com o Weblogic 10.3.

Quartz Framework

É uma solução open source de agendamento e execução de tarefas. Estas tarefas são chamadas de jobs no Quartz. A solução prove a possibilidade de realizar agendamentos complexos usando expressões chamadas de Cron Expressions que são semelhantes as expressões do sistema operacional Linux.

O Quartz pode ser usando em cluster e em transações usando distribuídas com JTA. A lista completa das funcionalidades você pode conferir aqui. O Spring também prove facilidades na utilização do Quartz.

Agora vamos ao projeto que criei para mostrar como podemos expor os serviços de uma aplicação com o Spring estes serviços vão ser consumidos pelo Quartz em um job via JConsole vamos poder parar e iniciar esta aplicação. Vamos lá então.

DateService

package com.blogspot.diegopacheco.springjmx.service;

/**
* Interface de contrato que prove as funções do serviço de datas.
*
* @author Diego Pacheco
* @since 13/07/2009
* @version 1.0
*
*/
public interface DateService {
public String getDate();
}

Bom essa é a interface do serviço. Eu sempre uso esse serviço pela simplicidade e facilidade para o entendimento da solução. Na prática poderia ser qualquer contrato de serviço :).

DateServiceImpl

package com.blogspot.diegopacheco.springjmx.service;

import java.util.Date;

/**
* Interface de contrato que prove as funções do serviço de datas.
*
* @author Diego Pacheco
* @since 13/07/2009
* @version 1.0
*
*/
public class DateServiceImpl implements DateService {

@Override
public String getDate() {
return new Date().toString();
}

}



A implementação do serviço é simples, só retorna um data. Agora vamos ver mais algumas coisas da aplicação como esta outra interface que segue a baixo.

ControlBean

package com.blogspot.diegopacheco.springjmx.service;

/**
* Interface de controle que sera exposta via JMX, seu proposito eh parar algum serviço
* e bem como iniciar o serviço que foi parado.
*
* @author Diego Pacheco
* @since 13/07/2009
* @version 1.0
*
*/
public interface ControlBean {
public void stop();
public void start();
}

Esta interface defini os métodos para que um serviço exposto via JMX possa ser parado e iniciado novamente. Vamos ver como seria esta implementação.

ControledBridgeDateService

package com.blogspot.diegopacheco.springjmx.service;

/**
* Pattern Bridge que utiliza o service de DateService com suporte a ControlBean
* Sendo capaz de não acessar o service caso a aplicacao esteja parada.
*
* @author Diego Pacheco
* @since 18/07/2009
* @version 1.0
*
*/
public class ControledBridgeDateService implements DateService, ControlBean{

private boolean doit = true;
private DateService ds;

@Override
public void start() {
doit = true;
}

@Override
public void stop() {
doit = false;       
}

@Override
public String getDate() {
if (doit)
return ds.getDate();
throw new ApplicationStopedException("Aplicação foi parada. Este método não pode ser executado neste momento");
}

public void setDs(DateService ds) {
this.ds = ds;
}   
}


O Serviço a cima implementa o pattern Bridge do GOF para a interface DateService. Este serviço também implementa ControlBean, logo ele pode ser parado. Este controle é feito por uma variável booleana que se chama 'doit' que por padrão é true.

Quando alguem invocar o método stop esta variável fica como o valor false e ao invocarem o método start esta variável volta ao valor true. A variável 'doit' é levada em considerações no método getDate() que delega para o método getDate da implementação de DateService caso contrário é levantada uma exception indicando que a aplicação foi parada e não poder ser executada.

Vamos a configuração do Spring, vamos expor estes serviços como MBeans do JMX, confira o xml a baixo:

















     




A exposição é realizada pelo MBeanExporter do Spring.A estratégia que estou usando vai expor todos os métodos públicos dos beans que forem informados ao MBeanExporter. Existem outras estratégias e outras formas de expor seus serviços como MBeans para isto confira na documentação oficial do spring.

Agora vamos a parte do Quartz, aqui existe uma pegadinha. Por que o Spring instância os jobs do quartz de forma diferente que ele instância seus beans. Logo a injeção de dependências não vai funcionar para resolver este problema criei uma classe utilitária que se chama ApplicationContextHolder.

package com.blogspot.diegopacheco.springquartz.job;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
* Bean que eh resposavel por segurar a instancia de ApplicationContext do Spring.
*
* @author Diego Pacheco
* @since 18/07/2009
* @version 1.0
*
*/
public class ApplicationContextHolder implements ApplicationContextAware{

private ApplicationContext ac;

@Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
this.ac = ac;
}

public ApplicationContext getApplicationContext(){
return ac;
}

}


Esta classe helper nada mais faz do que implementar ApplicationContextAware e expor o ApplicationContext do Spring através de um método getter.

Esta classe é passado ao Quartz via um Map e você pode recupera-la de dentro do seu Job é isto que vou mostrar agora na classe a baixo.

DateJob

package com.blogspot.diegopacheco.springquartz.job;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import com.blogspot.diegopacheco.springjmx.service.ApplicationStopedException;
import com.blogspot.diegopacheco.springjmx.service.DateService;

/**
* Job do Quartz que chama o serviço de datas. Esta classe usa apenas a interface do service. Como instancia
* eh injetado o ControledBridgeDateService que eh uma bridge para implemantacao e pode ser pausado via JMX.
*
* @author Diego Pacheco
* @since 18/07/2009
* @version 1.0
*
*/
public class DateJob extends QuartzJobBean {

private DateService getDateService(JobExecutionContext jec) {
try {

ApplicationContextHolder ach = (ApplicationContextHolder) jec.getScheduler().getContext().get("applicationContextHolder");
return (DateService) ach.getApplicationContext().getBean("controledBridgeDateService");

} catch (SchedulerException e) {
throw new RuntimeException("Erro ao objeto o parametro 'applicationContextHolder' do contexto do scheduler do quartz. ");
}
}

@Override
protected void executeInternal(JobExecutionContext jec)    throws JobExecutionException {
try{

System.out.println("DataService: " + getDateService(jec).getDate() );

}catch(ApplicationStopedException ase){
System.out.println(ase.getMessage());
}

}

}


Este job do quartz usa o serviço de datas através da interface DateService. Ele obtém este bean com o bean utilitário que falei acima no método getDateService(). No final das contas o service que esta sendo utilziado por este job não é o DateServiceImpl que foca apenas na implementação do contrato e na lógica de negócio. O bean utilizado aqui é o ControledBridgeDateService que além de chamar o serviço de datas real implementa o contrato para parar e iniciar este serviço.

Sempre é uma boa prática de design seprar este tipo de código do código de negócio eu fiz isto usando a classe ControledBridgeDateService através do pattern bridge do GOF.

Agora vamos ver a configuração do Spring, confira o XML a baixo:
































JobDetailBean é uma classe utilitária do Spring para criarmos jobs do Quartz. Você indica o job a ser criado pela propriedade jobClass. Depois é configurado um CronTriggerBean que agenda a sexecução de um JobDetailBean com um cron expression. Por fim configuramos um SchedulerFactoryBean informado o CronTrigger configurado anteriormente e através do atributo schedulerContextAsMap passamos detalhes para o contexto do job do quartz, neste caso apénas é passado o applicationContextHolder que é a classe utiliária para obter o contexto do Spring de dentro de job do quartz.

OK. Agora é só fazer o deploy da aplicação no Weblogic. Para isso usei maven conforme a configuração do POM que segue a baixo.


4.0.0
com.blogspot.diegopacheco.sandbox.java
spring-jmx-weblogic-ear
spring-jmx-weblogic-ear
1.0-SNAPSHOT
ear
spring-jmx-weblogic-ear




org.apache.maven.plugins
maven-compiler-plugin

1.6
1.6
UTF-8



true
org.apache.maven.plugins
maven-source-plugin


attach-sources

jar






org.apache.maven.plugins
maven-ear-plugin
2.3.2

spring-jmx-weblogic-ear
spring-jmx-weblogic-ear
1.4


com.blogspot.diegopacheco.sandbox.java
spring-jmx-weblogic
spring-jmx-weblogic





org.codehaus.cargo
cargo-maven2-plugin


weblogic103x
D:\\Diego\\Java\\bin\\ORACLE\\bea\\wlserver_10.3
${project.build.directory}/cargo.log
${project.build.directory}/output.log


existing
D:\\Diego\\Java\\bin\\ORACLE\\bea\\wlserver_10.3\\samples\\domains\\wl_server

wl_server
wl_server
weblogic
weblogic
80
high





start-container
pre-integration-test

deploy









com.blogspot.diegopacheco.sandbox.java
spring-jmx-weblogic
1.0-SNAPSHOT
war






Eu criei dois projeto do maven são eles:
  • spring-jmx-weblogic: Que é uma aplicação Web, os fontes que mostrei neste post estão neste projeto.
  • spring-jmx-weblogic-ear: É outro projeto que contem o empacotamento e o deploy do EAR no Weblogic, o pom que você viu acima fica aqui.
Agora basta rodar: mvn clean install nestes dois projetos e pronto. Apénas coisa para que o Weblogic ja esteja no ar quando você rodar o clean install do projeto spring-jmx-weblogic-ear.

Agora abra o JConsole. Você faza isso executando o jconsole.exe que fica na pasta bin da instalação do seu JDK. Conecte no processo local chamado de weblogic.Server. Va na sessão dos MBeans e procure por bean, lá você vai ver os beans expotos pelo Spring.

Você pode testar indo em ControledBridgeDateService e operations, que na prática são os métodos que foram expostos via Spring. Primeiro clique em stop e verifique se a aplicação mudou a mensagem no log do Weblogic e depois clique em start a mensagem deve mudar de novo.


JConsole conectado no Weblogic, log do Weblogic ao fundo


Spring sempre prove facilidades para integração do seu modelo de desenvolvimento de serviços com as tecnologias java como JMX e soluções abertas como o Quartz, você pode usar essa solução para diversos aspectos de sua aplicação em termos de monitoramento e gerenciamento.

Se você quiser pode obter os fontes completos da aplicação no meu repositório do Subversion nos seguintes endereços:
Até a próxima, abraços.

Popular posts from this blog

Kafka Streams with Java 15

Rust and Java Interoperability

HMAC in Java