Introdução
Nesse décimo quarto post vou mostrar como configurar o tenant automaticamente usando a URL. Vou criar uma lógica de cadastro de tenant onde será configurado qual URL pertence aquele tenant. Com isso não será mais necessário informar o parâmetro de multTenantId pela URL. Vou mostrar também o recurso de bundle por tenant, para que uma chave possa conter um valor especifico para um determinado tenant.
Lógica Tenant
Então agora vou criar um CRUD de Tenant para fazer essa configuração, vou definir a estrutura através do package-info.
package-info.java:
@JArchGenerateLogicCrud(nameSubPackage = "tenant",
master = @JArchGenerateMaster(name = "Tenant", tableName = "tb_tenant",
fields = {
@JArchGenerateField(fieldName = "numero", fieldTable = "nr_tenant",
description = "Número", type = FieldType.NUMBER, codeLookup = true,
required = true, exclusive = true,
search = @JArchGenerateSearch(row = 1, column = 1, span = 3),
xhtml = @JArchGenerateXhtml(rowDataXhtml = 1, columnDataXhtml = 1, showDataTableList = true)),
@JArchGenerateField(fieldName = "url", fieldTable = "nm_url", description = "URL",
type = FieldType.NAME, descriptionLookup = true, required = true, exclusive = true,
search = @JArchGenerateSearch(row = 1, column = 3, span = 6),
xhtml = @JArchGenerateXhtml(rowDataXhtml = 2, columnDataXhtml = 1, showDataTableList = true))
}
)
)
Para gerar o código executo o MAVEN com os steps CLEAN COMPILE PACKAGE ou pela IDE:
A primeira coisa a fazer depois da geração do código é alterar a classe pai do TenantEntity, alterando o CrudMultitenantEntity para CrudEntity. Isso pelo fato dessa entidade ser a definição de multitenant.
@JArchLookup(codeAttribute = "numero", descriptionAttribute = "url")
@Audited
@Table(name = "tb_tenant",
indexes = {
@Index(columnList = "nr_tenant", name = "dx_tenantnrten"),
@Index(columnList = "nm_url", name = "dx_tenantnmurl")
})
@Entity(name = "tenant")
@SequenceGenerator(name = "TenantIdSequence", sequenceName = "sq_idtenant", allocationSize = 1)
public class TenantEntity extends CrudEntity {
A definição da lógica do tenant é bem simples, estou criando 2 atributos, número e url. Como essa lógica é para utilização do administrador do sistema, não vou adicionar no menu, então para acessa-lá será necessário digitar na URL o caminho da página list.
Vou criar 4 definições de tenant's, sendo que cada um é um cliente diferente que usa a aplicação, sendo ele WESA, DSF, BSMartins e Nutrir. Cada um deles é um cliente diferente que utiliza nossa aplicação e não existe nenhum vínculo entre eles. Pra cada um deles terá uma URL específica:
Redirecionamento URL
No ambiente de produção essas URL's deveriam ser válidas e configuradas pelo DNS, mas no meu caso como é um ambiente de demonstração vou configurar meu arquivo de hosts para que cada uma dessas urls aponte pra localhost:
hosts:
Identificando URL
Na fachada da lógica de tenant vou criar um método para setar o tenant no contexto do JARCH de acordo com a URL passado por parâmetro:
TenantFacade.java:
public class TenantFacade extends CrudFacade<TenantEntity, ITenantManager> {
public void configTenant(String url) {
clientJpaql()
.orderByAsc(TenantEntity_.numero)
.where()
.contains(TenantEntity_.url, url)
.collect()
.singleOptional()
.ifPresent(t -> getMultiTenant().set(t.getNumero()));
}
}
E coloco a chamada da verificação do tenant na criação da sessão através do SessionListener.
@WebListener
public class SessionListener implements HttpSessionListener {
@Inject
private TenantFacade tenantFacade;
@Inject
private AlertaFacade alertaFacade;
@Inject
private MensagemFacade mensagemFacade;
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
tenantFacade.configTenant(JsfUtils.getServerName());
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
//
}
}
E acessando a URL nutrir.gcpr.com.br:8080/contas o sistema automaticamente identifica o tenant 4:
Bundle Específico
Agora nossa aplicação possuí 4 clientes (WESA, DSF, BSMartins e NUTRIR). Nosso cliente DSF quer utilizar a nomenclatura Conta Caixa no lugar de Categoria. Para resolver isso vamos criar um bundle_pt_BR_2.properties, repare que o "2" no nome do properties se refere ao tenant. Nesse bundle criado vamos definir a chave label.categoria=Conta Caixa.
bundle_pt_BR_2.properties:
label.categoria=Conta Caixa
Repare que agora temos 2 arquivos de bundle, um geral bundle_pt_BR.properties e um específico para o tenant 2 bundle_pt_BR_2.properties. No bundle específico só é adicionado as chaves específicas, que nesse caso somente o label.categoria. Agora ao executar a aplicação com a URL da DSF temos o seguinte:
Repare que tanto o menu e a tela está como Conta Caixa onde era Categoria. Isso funciona porque o JARCH verifica primeiro no bundle específico. E entrando como WESA temos:
Agora pra WESA volta a ser Categoria que é a definição do bundle padrão.
Alterando Sigla/Nome Empresa
Agora que mostrei como configurar uma chave no bundle por tenant, vou alterar a Sigla e Nome da Empresa (SGE e Nome Empresa) para ficar com a sigla e nome da empresa que está utilizando a aplicação. Então para a WESA (Tenant 1) vou criar o bundle específico e adicionar as chaves de sigla e nome da empresa:
bundle_pt_BR_1.properties:
label.siglaEmpresa=WESA
label.nomeEmpresa=WESA Inform\u00E1tica Ltda
E repare que no rodapé da aplicação mudou o nome da empresa:
Pra DSF vou fazer a mesma coisa:
bundle_pt_BR_2.properties:
label.categoria=Conta Caixa
label.siglaEmpresa=DSF
label.nomeEmpresa=Desenvolvimento de Sistemas Fiscais
E repare que no rodapé da aplicação mudou o nome da empresa:
Pra BSMartins vou fazer a mesma coisa:
bundle_pt_BR_3.properties:
label.siglaEmpresa=BSM
label.nomeEmpresa=Business San Martins
E repare que no rodapé da aplicação mudou o nome da empresa:
Pra NUTRIR vou fazer a mesma coisa:
bundle_pt_BR_4.properties:
label.siglaEmpresa=NUTRIR
label.nomeEmpresa=Nutrir Org\u00E2nicos
E repare que no rodapé da aplicação mudou o nome da empresa:
Conclusão
Nesse post mostrei como configurar o tenant na aplicação através da URL. Mostrei também como utilizar chaves específicas do bundle por tenant. Esses são recursos muito úteis que o JARCH disponibiliza evitando ter que fazer IF's para configuração de labels. Em relação ao multitenant nos poupa de ter que criar um banco de dados para cada cliente diferente que utiliza a aplicação.
Segue o link dessa video aula: https://youtu.be/V5QoQnP50kQ
Até mais,
Nenhum comentário:
Postar um comentário