Introdução
Nesse trigésimo post vou refatorar para deixar o código coeso, colocando as regras em sua devida camada. Vou atualizar também para a versão 20.12.0-SNAPSHOT.
Atualizando Versão
A primeira alteração que vou fazer é atualizar para a versão 20.12.0-SNAPSHOT.
pom.xml:
pom.xml:
<properties>
<jarch-version>20.12.0-SNAPSHOT</jarch-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
persistence.xml (web, job e ws)
<jar-file>lib/contas-client-1.0.0.jar</jar-file>
<jar-file>lib/jarch-core-20.12.0-SNAPSHOT.jar</jar-file>
LancamentoObserver
Agora vou mover as regras de negócio que está dentro do LancamentoObserver, movendo para métodos dentro da camada Service. A primeira alteração é mover a codificação do evento @JArchEventCreateEntity para um método dentro do Service.
LancamentoObserver:
LancamentoObserver:
@Inject
private LancamentoService lancamentoService;
private void criacaoLancamento(@Observes @JArchEventCreateEntity LancamentoEntity lancamento) {
lancamentoService.configuraLancamentoInclusao(lancamento);
}
LancamentoService:
@JArchParameter
@Inject
private ParametroBancoDefault parametroBancoDefault;
@JArchParameter
@Inject
private ParametroCentroCustoDefault parametroCentroCustoDefault;
@JArchParameter
@Inject
private ParametroCategoriaDefault parametroCategoriaDefault;
@JArchParameter
@Inject
private ParametroPessoaDefault parametroPessoaDefault;
@JArchParameter
@Inject
private ParametroUtilizaBpmAutorizacaoLancamento parametroUtilizaBpmAutorizacaoLancamento;
public void configuraLancamentoInclusao(LancamentoEntity lancamento) {
lancamento.setAberto(true);
lancamento.setBanco(parametroBancoDefault.getValue());
lancamento.setCentroCusto(parametroCentroCustoDefault.getValue());
lancamento.setCategoria(parametroCategoriaDefault.getValue());
lancamento.setPessoa(parametroPessoaDefault.getValue());
boolean autorizado = !parametroUtilizaBpmAutorizacaoLancamento.getValue()
|| getUserInformation().get(UsuarioEntity.class).getCargo() != CargoType.OPERADOR;
lancamento.setAutorizado(autorizado);
}
Agora vou refatorar o código que está no evento de clonagem para dentro do Service.
LancamentoObserver:
private void clonaLancamento(@Observes @JArchEventCloneEntity LancamentoEntity lancamento) {
lancamentoService.configuraLancamentoClonagem(lancamento);
}
LancamentoService:
public void configuraLancamentoClonagem(LancamentoEntity lancamento) {
lancamento.setAberto(true);
boolean autorizado = !parametroUtilizaBpmAutorizacaoLancamento.getValue()
|| getUserInformation().get(UsuarioEntity.class).getCargo() != CargoType.OPERADOR;
lancamento.setAutorizado(autorizado);
}
Agora vou refatorar o código que está no evento de validação na inclusão ou alteração para dentro do Service.
LancamentoObserver:
private void validaDataInferiorDezAnos(@Observes @JArchEventValidInsertChange LancamentoEntity lancamento) {
lancamentoService.validaInclusaoAlteracao(lancamento);
}
LancamentoService:
public void validaInclusaoAlteracao(LancamentoEntity lancamento) {
LocalDate vencimentoMinimo = LocalDate.now().minusYears(10).withMonth(1).withDayOfMonth(1);
if (lancamento.getVencimento().isBefore(vencimentoMinimo)) {
throw new ValidationException(BundleUtils.messageBundle("message.vencimentoPartirDe",
DateUtils.formatddMMyyyy(vencimentoMinimo)));
}
}
Agora vou refatorar o código que está no evento de após a inclusão ou alteração para dentro do Service.
LancamentoObserver:
private void atualizaAlertaMensagemInclusaoAlteracao(@Observes @JArchEventInsertChange(momentPersistMerge = MomentType.AFTER) LancamentoEntity lancamento) {
lancamentoService.atualizaAlertaMensagemInclusaoAlteracao();
}
LancamentoService:
public void atualizaAlertaMensagemInclusaoAlteracao() {
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();
}
Agora vou refatorar o código que está no evento de após a exclusão ou alteração para dentro do Service.
LancamentoObserver:
private void atualizaAlertaMensagemExclusao(@Observes @JArchEventDelete(momentRemove = MomentType.AFTER) LancamentoEntity lancamento) {
lancamentoService.atualizaAlertaMensagemExclusao();
}
LancamentoService:
public void atualizaAlertaMensagemExclusao() {
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();
}
Agora vou refatorar o código que está no evento de após a inclusão que está instanciando o fluxo para dentro eo Service.
LancamentoObserver:
private void instanciaFluxoLancamentoOperador(@Observes @JArchEventInsert(momentPersist = MomentType.AFTER)
LancamentoEntity lancamento) {
lancamentoService.instanciaFluxoLancamentoOperador(lancamento);
}
LancamentoService:
public void instanciaFluxoLancamentoOperador(LancamentoEntity lancamento) {
if (getUserInformation().get(UsuarioEntity.class).getCargo() != CargoType.OPERADOR) {
return;
}
if (!parametroUtilizaBpmAutorizacaoLancamento.getValue()) {
return;
}
runtimeService
.createProcessInstanceByKey(Constant.PROCESS_AVALIACAO_CONTA)
.businessKey(lancamento.getId().toString())
.setVariable(Constant.ID_MULTITENANT, getMultiTenant().get())
.setVariable(Constant.ID_OPERADOR, getUserInformation().get().getId())
.setVariable("descricao", lancamento.getDescricao())
.setVariable("valor", lancamento.getValor().doubleValue())
.setVariable("vencimento", lancamento.getVencimento())
.execute();
}
Agora vou refatorar o código que está no evento de ação dinâmica de avaliar conta para dentro do Service.
LancamentoObserver:
private void avaliacao(@Observes @JArchEventDynamicAfter(Constant.ID_AVALIAR_LANCAMENTO) LancamentoEntity lancamento) {
lancamentoService.avaliacao(lancamento);
}
LancamentoService:
public void avaliacao(LancamentoEntity lancamento) {
UsuarioEntity usuarioLogado = getUserInformation().get(UsuarioEntity.class);
String variavelAprovacao;
if (usuarioLogado.getCargo() == CargoType.SUPERVISOR) {
variavelAprovacao = "aprovadoSupervisor";
} else if (usuarioLogado.getCargo() == CargoType.GERENTE) {
variavelAprovacao = "aprovadoGerente";
} else {
variavelAprovacao = "aprovadoDiretor";
}
taskService.complete(lancamento.getTask().get().getId(),
Map.of(variavelAprovacao, lancamento.isAprovado(),
"valorLancamento", lancamento.getValor().doubleValue()));
}
LancamentoService
Agora vou ajustar os métodos depreciados, clientJpaql() para getClientJpql().
LancamentoObserver:
public Collection<LancamentoEntity> contaPagarAbertoAte(LocalDate vencimento) {
return getClientJpql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.equalsTo(LancamentoEntity_.autorizado, true)
.and()
.lessOrEqualsThan(LancamentoEntity_.vencimento, vencimento)
.collect()
.list();
}
public Collection<LancamentoEntity> contaPagarAbertoEm(LocalDate vencimento) {
return getClientJpql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.equalsTo(LancamentoEntity_.autorizado, true)
.and()
.equalsTo(LancamentoEntity_.vencimento, vencimento)
.collect()
.list();
}
Agora vou ajustar o método depreciado, find() para getClientJpql().find().
LancamentoService:
public void autorizaConta(Long idLancamento) {
LancamentoEntity lancamento = getClientJpql().find(idLancamento);
lancamento.setAutorizado(true);
change(lancamento);
}
Métodos Depreciados
Agora vou ajustar os métodos depreciados:
De clientJpaql() para getClientJpql()
De count() para getClientJpql().count()
De searchUniqueFilter() para getClientJpql().searchUniqueFilter()
De ICrudListAction para ICrudListController
SonarLint
Agora vou chamar o SonarLint para verificar as sugestões de melhorias no LancamentoService.
1. A primeira sugestões que ele propôs foi que existem dois métodos duplicados, sugerindo deixar somente um. Então vou renomear o método atualizaAlertaMensagemExclusao() para atualizaAlertaMensagem() e vou excluir o atualizaAlertaMensagemInclusaoAlteracao().
2. Agora ele está sugerindo que isPresent() em um Optional para acessar o valor.
lancamento
.getTask()
.ifPresent(t -> taskService.complete(t.getId(),
Map.of(variavelAprovacao, lancamento.isAprovado(),
"valorLancamento", lancamento.getValor().doubleValue())));
3. Agora está sugerindo para utilizar o Boolean.TRUE.
if (!Boolean.TRUE.equals(parametroUtilizaBpmAutorizacaoLancamento.getValue())) {
return;
}
4. Agora está sugerindo para utilizar o try resources.
public StreamedContent downloadComprovanteBaixa(LancamentoEntity lancamento) {
Document document = new Document();
try {
File file = File.createTempFile("comprovante", ".pdf");
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
PdfWriter.getInstance(document, fileOutputStream);
document.open();
document.add(new Paragraph("Comprovante de Pagamento Conta " + lancamento.getTipoLancamento().getDescription()));
document.add(new Paragraph("Codigo......: " + lancamento.getCodigo()));
document.add(new Paragraph("Descrição...: " + lancamento.getDescricao()));
document.add(new Paragraph("Banco.......: " + lancamento.getBanco().getCodigo() + " - " + lancamento.getBanco().getNome()));
document.add(new Paragraph("Centro Custo: " + lancamento.getCentroCusto().getCodigo() + " - " + lancamento.getCentroCusto().getDescricao()));
document.add(new Paragraph("Categoria...: " + lancamento.getCategoria().getCodigo() + " - " + lancamento.getCategoria().getDescricao()));
document.add(new Paragraph("Pessoa......: " + CpfCnpjUtils.formataCpfCnpj(lancamento.getPessoa().getCpfCnpj()) + " - " + lancamento.getPessoa().getNome()));
document.add(new Paragraph("Vencimento..: " + DateUtils.formatddMMyyyy(lancamento.getVencimento())));
document.add(new Paragraph("Valor.......: " + NumberUtils.formatMoney(lancamento.getValor())));
document.add(new Paragraph("Código de autenticação: " + Md5Utils.generate(NumberUtils.formatZeroLeft(lancamento.getId(), 10))));
document.close();
return new DefaultStreamedContent(new FileInputStream(file), FileType.PDF.getContentType(), "comprovante.pdf");
}
} catch (DocumentException | IOException ioe) {
throw new ValidationException("Erro na geração do comprovante...");
} finally {
document.close();
}
}
5. E por último está pedindo para acertar a chamada do código depreciado new DefaultStreamedContent.
try (FileInputStream fileInputStream = new FileInputStream(file)) {
return DefaultStreamedContent
.builder()
.stream(() -> fileInputStream)
.contentEncoding(FileType.PDF.getContentType())
.name("comprovante.pdf")
.build();
}
Referência Circular
Com as refatorações anteriores causei um problema de referência circular do CDI. Agora minha classe LancamentoService faz uma injeção ao AlertaFacade e o MensagemFacade, e por sua vez tanto o AlertaFacade quando o MensagemFacade faz uma injeção ao LancamentoService. Para resolver isso vou mover os métodos do AlertaFacade e MensagemFacade para o LancamentoService, vou excluir as classes AlertaFacade e o MensagemFacade e ajustar os pontos na aplicação que faziam referência as essas classes para utilizar o LancamentoService.
LancamentoService.java:
public class ComunicacaoObserver {
@Inject
private LancamentoService lancamentoService;
private void inclusaoAlteracao(@Observes @JArchEventInsertChange(momentPersistMerge = MomentType.AFTER) CommunicationEntity communicationEntity) {
lancamentoService.atualizaAlertaMensagem();
}
private void exclusao(@Observes @JArchEventDelete(momentRemove = MomentType.AFTER) CommunicationEntity communicationEntity) {
lancamentoService.atualizaAlertaMensagem();
}
}
ComunicacaoObserver.java:
public class ComunicacaoObserver {
@Inject
private LancamentoService lancamentoService;
private void inclusaoAlteracao(@Observes @JArchEventInsertChange(momentPersistMerge = MomentType.AFTER) CommunicationEntity communicationEntity) {
lancamentoService.atualizaAlertaMensagem();
}
private void exclusao(@Observes @JArchEventDelete(momentRemove = MomentType.AFTER) CommunicationEntity communicationEntity) {
lancamentoService.atualizaAlertaMensagem();
}
}
LoginObserver.java:
public class LoginObserver {
private void login(@Observes @JArchEventLoginAfter UsuarioEntity usuario, LancamentoService lancamentoService) {
lancamentoService.atualizaAlertaMensagem();
if (usuario.getCargo().equals(CargoType.DIRETOR) || usuario.getCargo().equals(CargoType.GERENTE)) {
JsfUtils.getSession().setMaxInactiveInterval(600);
}
}
}
SessionListener.java:
@WebListener
public class SessionListener implements HttpSessionListener {
@Inject
private TenantService tenantFacade;
@Inject
private LancamentoService lancamentoService;
@Inject
private UsuarioService usuarioFacade;
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
tenantFacade.configTenant(JsfUtils.getServerName());
usuarioFacade.criaUsuarioAdmin();
lancamentoService.atualizaAlertaMensagem();
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
LogUtils.warning("SESSION TIMEOUT !!!");
}
}
Conclusão
Nesse post fiz a atualização do projeto para a versão mais atual do JARCH (na publicação desse Post), e algumas melhorias no código. Melhorias no código devem ser frequentemente aplicadas, principamente quando o código sofre constantes alterações ou implementações. Uma boa prática de programação é sempre manter o código mais simples possível, ser auto documentado, utilização de boas práticas de programação orientada a objetos, buscando sempre a coesão e desacoplamento.
Até mais,