Não Separe Criação de Arte e Desenvolvimento de Software

Depois de tantos anos desenvolvendo software, ainda não tenho uma experiencia de sucesso atuando em processos que separam a criação de interfaces gráficas da implementação das funcionalidades do sistema. Sinceramente, nunca comprei a ideia de linha de produção de software. Certa vez recebi uma interface gráfica que deveria apresentar alguns produtos pré-filtrados para o usuário. Era para apresentar o primeiro produto disponível no banco de dados. Naquele caso, eu conhecia o negócio melhor que o designer. Argumentei que isso poderia induzir o usuário ao erro, pois não trabalhávamos com Big Data e nem tínhamos quaisquer dados históricos para utilizar. Para o designer, o importante era a beleza de sua “obra de arte” em detrimento do negócio. Foi implementado do jeito que foi concebido, pois o designer tinha total apoio das áreas comerciais.

Analise essas duas bicicletas e escolha uma delas para dar uma volta pela cidade:

Figura 1 – A bicicleta conceitual do designer

Figura 2 – A bicicleta funcional do engenheiro

Obviamente, você escolheria a primeira porque é mais bonita, parece ser a mais veloz, com certeza é a mais leve e “causa” por onde quer que passe. Todavia, não parece estranho a ausência de raios cruzados tangenciando os cubos para transmissão do torque? E cadê os cubos? A bicicleta com roda maior tem maior velocidade angular. Se não houve preocupação com os aspectos mecânicos, essa bicicleta não é funcional. Pensando bem, desde quando um designer se preocupa com esses “detalhes insignificantes que inibem a criação”? Seria um pesadelo de engenharia tentar desenvolvê-la e qualquer tentativa de alterá-la para que ela exerça sua função primordial iria transformar a obra de arte do designer em uma Quimera. Do jeito que está, essa bicicleta serve apenas para ser pendurada na parede.

Por que as obras de arte são bem sucedidas se observarmos apenas o aspecto da criação? Uma obra de arte não precisa voar, submergir à algumas léguas de profundidade, ultrapassar a barreira do som e nem suportar uma viagem pelo espaço até a galáxia de Andrômeda. Uma obra de arte já nasce pronta e atinge seu objetivo quando a aceitação do público estiver de acordo com o que o artista esperava. Não é assim que funciona em desenvolvimento de software. Tal como nos artistas, uma das grandes habilidade de um designer é a criatividade. Um desenvolvedor de software costuma ser cético e racional para não dizer conservador quando se depara com algo novo. No livro A Cabeça de Steve Jobs, é relatado um desafio que ele lançou aos projetistas de placas: criarem uma placa arredondada. Os projetistas ficaram incrédulos, pois o motivo mais óbvio para algo ser retangular é o aproveitamento de espaço. No final, a ideia foi abandonada. A “arte” do designer deve ser materializada em algo funcional para que atinja seus objetivos de negócio. É necessário o trabalho de web designers ou programadores frontend, programadores backend, analistas, infraestrutura e mais um monte de perfis para fazer a “obra de arte” se mover e até voar.

Quando um designer atua sozinho sem quaisquer interesses com as restrições do negócio, há poucas chances do produto final corresponder à concepção original. Além da lição de casa, que é a imersão no negócio, um designer deve ter a mente aberta. Ele deve ouvir e entender o que os desenvolvedores, os analistas de banco de dados e os demais técnicos têm a dizer para só então dar a última pincelada em sua obra. Além disso, é necessário que o designer se mostre flexível para modificar sua criação sempre que uma restrição de negócio ou normativa for encontrada nas etapas posteriores. Ignorei restrições de origem tecnológica porque isso dificilmente é aceito como desculpa para mudar o que quer que seja.

A criação deveria ser apenas um norte mal acabado que serve para orientar o início de um projeto. Aspectos visuais deveriam ser refinados de forma incremental à medida que um projeto avança para que a concepção não extrapole às necessidades apresentadas e nem vá contra o negócio. Em outras palavras, os designers podem e devem ter um pé no mundo das idéias, mas não podem esquecer que aqui no mundo real o desenvolvimento de software é um esforço colaborativo.

Figura 3 – Mentes unidas

Referências

1. THALITA. Relação Design Engenharia. Nexa Projetos. Disponível em: [http://nexaprojetos.com/blog/2017/07/18/relacao-design-engenharia/]. Acesso em 23 jan. 2019.

2. LOPATEGUI, Ed. Does every engineer have a right to a portfolio?. GrabCAD Blog. Disponível em: [https://blog.grabcad.com/blog/2015/08/03/does-every-engineer-have-a-right-to-a-portfolio/]. Acesso em 23 jan. 2019.

Anúncios

Como Criar Índices no Oracle

Bancos de dados podem se tornar gigantescos e encontrar um registro rapidamente se torna crítico. Um índice [4] é um método de otimização de performance que permite a rápida recuperação de registros do banco de dados ao ser vinculado a uma ou mais colunas de uma tabela. Índices não são visíveis para o usuário final [3], mas as presenças deles causam a diminuição, às vezes bastante evidente, do tempo de execução das consultas. Essa diminuição do tempo de acesso tem um ônus nas operações de atualização: leva-se mais tempo para atualizar uma tabela que possui índices do que uma tabela que não possui índices, pois os índices também precisam ser atualizados. Sendo assim, recomenda-se que os índices sejam criados somente para colunas que sejam frequentemente consultadas.

Criar um índice é bem fácil:

CREATE INDEX <INDEX_NAME>
ON <TABLE_NAME>
( <COLUMN1>, <COLUMN2>, ... );

Para criar um índice para a coluna NR_CPF na tabela TBL_PESSOA, é só fazer assim:

CREATE INDEX IXCPF
ON TBL_PESSOA
( NR_CPF );

Para remover o índice que criamos, basta executar o comando:

DROP INDEX IXCPF;

Modelos de Índice

Por padrão, os índices criados no Oracle são do tipo B-tree, que são balanceados para que todos os nós-folha tenham a mesma profundidade na árvore e o trabalho para acessá-los seja o mesmo. Cada nó folha nesse modelo de índice aponta para somente uma linha. O outro modelo de índice é o Bitmap. Nesse modelo, cada entrada aponta para mais de uma linha. Esse tipo de índice atribui uma série de 1 e 0 para cada linha para indicar se a linha contém (1) ou não (0) o valor em pelo menos uma das colunas que compõe o índice [1]:

Valor Rowid Início Rowid Fim Bitmap
VAL1 AAAA ZZZZ 001000000…
VAL2 AAAA ZZZZ 110000000…
VAL3 AAAA ZZZZ 000111100…

Quando todas as colunas de um índice são nulas, o banco de dados utiliza Bitmap e não B-tree. O problema do uso de bitmaps é que eles causam preempção da tabela: enquanto todas as colunas que compõe o índice não forem atualizadas, o lock da tabela não é liberado e não é possível manipular registros.

Tipos de Índice

O Oracle possui vários tipos de índice que implementam os modelos de índice mencionados.

Índice Baseado em Função

São índices compostos por uma função aplicada à coluna. O índice armazena o resultado do retorno daquela função:

CREATE INDEX IXNOME
ON TBL_PESSOA
( UPPER (NM_NOME) );

Se você estiver utilizando um índice baseado em função, sua cláusula WHERE deve ser idêntica à definição do índice:

(...)
WHERE UPPER (NM_NOME) = :UM_NOME

Índice Único

Índice único é um tipo de constraint que garante que apenas um valor de um determinado tipo seja armazenado na tabela. Normalmente, esse tipo de índice já é criado junto com a tabela:

CREATE TABLE TBL_PESSOA (
  (...)
   CONSTRAINT UK_AD_CPF UNIQUE (NR_CPF)
);

Índices Descendentes

Índices do tipo B-tree são estruturas ordenadas do menor valor para o maior valor, mas isso pode ser alterado se você especificar um DESC ou um ORDER BY em sua cláusula WHERE. O DESC explícito em um coluna que forma o índice impede que o Oracle inclua uma cláusula SORT ORDER BY implícita na consulta. Se a ordenação é custosa nessa tabela, você poupará um bom tempo.

Índices de Busca JSON

Armazenar dados no formato JSON é cada vez mais comum. Como os atributos são armazenados como parte do documento JSON e não em suas próprias colunas, a indexação é lenta.

Índices de Texto do Oracle

Se você tiver uma grande quantidade de texto armazenado em uma coluna, pode construir um índice de texto para possibilitar a criação de buscas mais complexas e análises semânticas. Há três tipos de índice de texto: contexto, categoria e regra.

Índices de Domínio da Aplicação

Às vezes, é necessário criar um índice especializado para uma aplicação, o que exige a customização de como um dado é indexado e armazenado.

Índices de Chave Reversa

Índices para colunas que armazenam valores incrementais – como chaves primárias baseadas em sequences – podem causar preempção da tabela uma vez que as possíveis muitas sessões abertas podem estar tentando consultar informações para armazenar seus próprios valores. Índices de chave reversa evitam esse problema invertendo a ordem do valor armazenado: ao invés de armazenar “1234”, armazena-se “4321”. Sendo assim, esses índices só servirão para fazer comparações de igualdade uma vez que não estão sendo armazenados em suas ordens naturais.

Como Decidir Criar um Índice

Não há uma receita nem uma resposta certa, mas você deve levar em consideração a velocidade da obtenção do registro e o impacto sistêmico. Além disso, para que o banco de dados utilize o índice, a coluna relacionada a ele deve estar na sua consulta.

Mais importante do que a quantidade de linhas obtidas é levar em conta quantos bloqueios de banco de dados você causa. O ideal é que uma consulta retorne um monte de registros sem fazer full table scan, o que é bem lento.

Se você trabalha com índices compostos, é importante que você decida sabiamente qual a ordem das colunas que compõe o índice, pois o Oracle lê um índice a partir da coluna que fica mais a esquerda, a qual interpretamos como sendo a primeira coluna do índice. Para índices compostos, esse guia é útil:

  1. Colunas que possuam condições de igualdade (=) devem ser as primeiras do índice
  2. Colunas com condições de intervalo (=, BETWEEN, etc) devem estar mais pra o final do índice
  3. Colunas que só aparecem em SELECT ou em ORDER BY deveriam ficar no final do índice

Referências

1. SAXON, Chris.How to Create and Use Indexes in Oracle Database. Oracle Blogs: All Things SQL. Disponível em: [https://blogs.oracle.com/sql/how-to-create-and-use-indexes-in-oracle-database]. Acesso em: 23 jan. 2019.

2. ORACLE. Database SQL Reference: CREATE INDEX. Oracle Help Center. Disponível em: [https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5010.htm]. Acesso em: 23 jan. 2019.

3. W3SCHOOLS. SQL CREATE INDEX Statement. w3schools.com Disponível em: [https://www.w3schools.com/sql/sql_create_index.asp]. Acesso em: 23 jan. 2019.

4. TECHONTHENET. Oracle / PLSQL: Indexes. TechOnTheNet. Disponível em: [https://www.techonthenet.com/oracle/indexes.php]. Acesso em: 23 jan. 2019.

Utilizando o Captcha Patchca

No Artigo utilizando o SimpleCaptcha em uma aplicação JSF, apresentei o SimpleCaptcha. Nesse artigo, vou mostrar, de forma mais genérica, como utilizar o Patchca em uma página HTML simples.

Primeiro, adicione a dependência abaixo no seu POM:

<dependency>
   <groupId>net.pusuo</groupId>
   <artifactId>patchca</artifactId>
   <version>0.5.0</version>
</dependency>

Em seguida, vamos criar um servlet que escreve a imagem do captcha na Response. Lembre-se de armazenar o valor gerado na sessão:

import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.patchca.color.SingleColorFactory;
import org.patchca.filter.predefined.CurvesRippleFilterFactory;
import org.patchca.service.ConfigurableCaptchaService;
import org.patchca.utils.encoder.EncoderHelper;

@WebServlet("/captcha")
public class Captcha extends HttpServlet {

	private ConfigurableCaptchaService cs = null;

	public Captcha() {
		cs = new ConfigurableCaptchaService();
		cs.setColorFactory(new SingleColorFactory(new Color(25, 60, 170)));
		cs.setHeight(57);
		cs.setWidth(140);
		cs.setFilterFactory(new CurvesRippleFilterFactory(cs.getColorFactory()));
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("image/png");
		OutputStream out = response.getOutputStream();
		String strCaptcha = EncoderHelper.getChallangeAndWriteImage(cs, "png", out);

		HttpSession sessao = request.getSession(true);
		sessao.setAttribute("MeuCaptcha", strCaptcha);

		out.flush();
		out.close();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}
}

Crie uma página HTML com um Javascript simples que faz a requisição de uma nova imagem para nosso servlet:

<html>
<head>
    <script type="text/javascript" src="/global/js/jquery-2.2.3.min.js"></script>
    <script>
        function recarregar() {
            $(".captcha").attr('src', '/captcha?J='+new Date().getTime());
            return false;
        }
    </script>
</head>
<body>
    <p>
        <img src="/captcha?J=1537211038494" class="captcha"> 
    </p>
    
    <p>
        <input type="captcha" name="captcha" maxlength="100" > 
        <a href="#" onclick="return recarregar()"  >Gerar outra imagem</a>
    </p>

    <p>
        <a href="#" >Enviar</a>
    </p>
</body>
</html>

A validação do valor do captcha digitado é igual à da outra implementação que fizemos: compare o valor da Request com o valor armazenado na sessão.

Categorias:Programação, Segurança Tags:

Criando um Tipo Customizado no Freemarker

O Apache FreeMarker permite que você crie um tipo customizado para formatar valores. Vamos criar um tipo para formatar o valor do salário de uma Pessoa.

public class Pessoa {
   private String nome;
   private Double salario ;
   public Pessoa(String nome, Double salario) {
      this.nome = nome;
      this.salario = salario;
   }
   public String getNome() {
      return nome;
   }
   public Double getSalario() {
      return salario;
   }
}

O template básico da página fica assim:

<html>
<body>
  <p>Pessoa: ${pessoa.nome}</p>
  <p>R$ ${pessoa.salario?string.@moeda}</p>
</body>
</html>

O código que cria e configura o formatador fica assim:

(...)
   Map<String, TemplateNumberFormatFactory> customNumberFormats = 
       new HashMap<String, TemplateNumberFormatFactory>();
   customNumberFormats.put("moeda", 
       new AliasTemplateNumberFormatFactory("0.00"));
   cfg.setCustomNumberFormats(customNumberFormats);
(...)

O template formatado fica assim:

<html>
<body>
  <p>Pessoa: Daniel</p>
  <p>R$ 10.000,00</p>
</body>
</html>

Bibliografia

1. [https://freemarker.apache.org/docs/ref_builtins_number.html]
2. [https://freemarker.apache.org/docs/pgui_config_custom_formats.html]

Compactando Diretórios em Java

Precisei fazer download de um diretório, mas antes do download esse diretório precisava ser compactado. Compactar um arquivo em Java utilizando a mecânica do Java 8 é bem fácil:

public static void pack(String sourceDirPath, String zipFilePath) throws IOException {
    Path p = Files.createFile(Paths.get(zipFilePath));
    try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(p))) {
        Path pp = Paths.get(sourceDirPath);
        Files.walk(pp)
          .filter(path -> !Files.isDirectory(path))
          .forEach(path -> {
              ZipEntry zipEntry = new ZipEntry(pp.relativize(path).toString());
              try {
                  zs.putNextEntry(zipEntry);
                  Files.copy(path, zs);
                  zs.closeEntry();
            } catch (IOException e) {
                System.err.println(e);
            }
          });
    }
}
Categorias:Programação Tags:, ,

Como Testar URLs HTTPS no Apache

Esse artigo é uma expansão do artigo sobre testes de virtual hosts no Apache. Nesse artigo, além de criar um virtual host e fazer um redirecionamento, geraremos um certificado SSL que o Apache enviará para o navegador. Rode o comando abaixo e preencha as informações referentes ao seu site. Importante: o campo Common Name, que é o campo CN do certificado digital, deve conter a URL do seu site.

C:\xampp\apache>makecert.bat
Generating a 2048 bit RSA private key
................................................................................
................................................................................
................................+++++
...+++++
writing new private key to 'privkey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:BR
State or Province Name (full name) [Some-State]:SP
Locality Name (eg, city) []:Sao Paulo
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Atitude Reflexiva
Organizational Unit Name (eg, section) []:Marketing
Common Name (e.g. server FQDN or YOUR name) []:marketing.atitudereflexiva.com.br
Email Address []:wm@atitudereflexiva.com.br

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:1234
An optional company name []:Atitude Reflexiva
Enter pass phrase for privkey.pem:
writing RSA key
Signature ok
subject=C = BR, ST = SP, L = Sao Paulo, O = Atitude Reflexiva, OU = Marketing, CN = marketing.atitudereflexiva.com.br, emailAddress = wm@atitudereflexiva.com.br
Getting Private key
        1 arquivo(s) movido(s).
        1 arquivo(s) movido(s).

-----
Das Zertifikat wurde erstellt.
The certificate was provided.

Pressione qualquer tecla para continuar. . .

Para verificar os dados do certificado gerado, você pode rodar o comando abaixo:

openssl x509 -in server.crt -noout -subject

Em seguida, instale o certificado para que o Sistema Operacional confie nele. Por último, adicione o virtualhost ao seu arquivo httpd.conf:

<VirtualHost marketing.atitudereflexiva.com.br:443>
    SSLProxyEngine on
    ProxyRequests Off
    ProxyPass "/aplicacao/"  "http://marketing.atitudereflexiva.com.br:8080/aplicacao/"
    ProxyPassReverse "/aplicacao/"  "http://marketing.atitudereflexiva.com.br:8080/aplicacao/"
    Redirect "/" "https://marketing.atitudereflexiva.com.br/aplicacao/"
    ServerName marketing.atitudereflexiva.com.br:443
    ServerAlias marketing.atitudereflexiva.com.br
    SSLEngine on
    SSLCertificateFile "c:/xampp/apache/conf/ssl.crt/server.crt"
    SSLCertificateKeyFile "c:/xampp/apache/conf/ssl.key/server.key"
</VirtualHost>

Obs.: De acordo com [4], [5] e [6], o Chrome não confia em certificados auto-assinados.

Referências

1. [https://httpd.apache.org/docs/current/mod/mod_ssl.html]
2. [https://gist.github.com/nguyenanhtu/33aa7ffb6c36fdc110ea8624eeb51e69]
3. [https://stackoverflow.com/questions/16430574/how-do-i-use-https-ssl-in-xampp-while-using-virtual-hosts]
4. [https://neard.io/doc/faq/#neterr_cert_authority_invalid-since-chrome-58]
5. [https://support.google.com/chrome/a/answer/7391219?hl=pt-BR]
6. [https://deliciousbrains.com/https-locally-without-browser-privacy-errors/]

Categorias:Infraestrutura Tags:, , , ,

Balanceamento de Carga com Apache e JBoss

Nesse artigo, utilizaremos o servidor HTTP Apache para balancear a carga entre diferentes instâncias do JBoss. O tipo de cluster que configuraremos é o load balance, mas as instâncias do JBoss não compartilharão recursos físicos para tratamento dos processos. Essas instâncias serão utilizadas apenas para oferecer redundância dos servidores de aplicação e também para permitir que a arquitetura fique escalável, pois basta adicionar novas instâncias do JBoss devidamente configuradas para que o Apache as reconheças e as inclua no grupo de balancing. Para simplicidade, mostraremos como fazer a configuração em modo standalone, mas você também pode ter esse comportamento no domain.

Estou utilizando o Apache 2.4.34 e o JBoss EAP 7.1.0.GA e a arquitetura ficará assim:

JBoss

Balanceamento de carga é a distribuição do tráfego de entrada entre servidores de aplicação. O balanceamento de carga aumenta a disponibilidade da aplicação garantindo que um servidor não receba muita carga e a aplicação permaneça disponível se um servidor de aplicação falhar. Historicamente, o JBoss AS herdava bibliotecas de balanceamento de carga do servlet container Apache Tomcat, que faziam parte do módulo web do servidor de aplicação. Esse módulo utilizava o mod_jk para conectar o Tomcat ao servidor web. Você pode trabalhar com o antigo mod_jk para configurar o balancing, mas as novas versões do JBoss trazem o mod_cluster, que melhorou a performance e a confiabilidade.

Vamos fazer as configurações do cluster no arquivo standalone-ha.xml, que é o profile padrão com capacidades de clusterização. O nome do nosso proxy será “meuproxy”:

<subsystem xmlns="urn:jboss:domain:modcluster:3.0">
   <mod-cluster-config advertise-socket="modcluster" 
      proxies="meuproxy" connector="ajp" >
      <dynamic-load-provider>
         <load-metric type="cpu"/>
      </dynamic-load-provider>
   </mod-cluster-config>
</subsystem>

(...)

<socket-binding-group name="standard-sockets" default-interface="public" 
   port-offset="${jboss.socket.binding.port-offset:0}">
   (...)
   <outbound-socket-binding name="meuproxy">
      <remote-destination host="localhost" port="8000"/>
   </outbound-socket-binding>
   (...)
</socket-binding-group>

Se você tiver problemas de bind porque há outros aplicativos na sua máquina utilizando portas que o JBoss esperava utilizar, basta modificar a propriedade jboss.socket.binding.port-offset, que por padrão tem o valor 0. Esse valor será incrementado nas portas definidas. Se a porta 8080 estiver configurada e a propriedade vale 1, então a porta passará a ser 8081 quando o JBoss for iniciado.

Apache

O Apache HTTP Server é configurado via arquivos de texto que ficam em locais diferentes de acordo com a instalação. Vamos alterar os arquivos httpd.conf e httpd-vhosts.conf.

Configuração do Arquivo httpd.conf

O arquivo httpd.conf contém as configurações padrão da aplicação. Nesse arquivo, há dois dos módulos que precisamos:

1. mod_proxy.so: implementa um proxy/gateway para o Apache HTTP Server e dá suporte a vários tipos de protocolos e algoritmos de balanceamento de carga. Módulos desenvolvidos por terceiros podem, devido a esse módulo, adicionar suporte a mais protocolos.

2. mod_proxy_ajp.so: esse módulo é utilizado no proxy reverso para um servidor de aplicação como o Tomcat utilizando o protocolo AJP13. Seu uso é similar a um proxy reverso HTTP, mas utiliza o prefixo ajp://.

Para utilizar esses módulos, basta descomentar as linhas abaixo:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so

É necessário que adicionemos alguns outros módulos que foram produzidos pela RedHat para dar suporte à integração com o JBoss:

1. mod_cluster_slotmem: provedor compartilhado de memória para os processos do Apache.

2. mod_manager: módulo que lê as informações do JBoss e atualiza as informações na memória compartilhada.

3. mod_proxy_cluster: é o módulo que roteia as requisições para o cluster.

4. mod_advertise: é um módulo adicional que permite ao Apache (httpd) notificar via multicast o IP e a porta onde o mod_cluster está ouvindo.

Para utilizar esses módulos, é necessário baixá-los, copiá-los para o diretório modules do seu Apache e adicionar as linhas abaixo:

LoadModule cluster_slotmem_module modules/mod_cluster_slotmem.so
LoadModule manager_module modules/mod_manager.so
LoadModule proxy_cluster_module modules/mod_proxy_cluster.so
LoadModule advertise_module modules/mod_advertise.so

Você também poderia baixar o código fonte e compilá-lo para sua plataforma de acordo com as informações desse artigo.

Configuração do Arquivo httpd-vhosts.conf

O termo Virtual Host se refere a rodar mais de um web site em uma mesma máquina como financeiro.atitudereflexiva.com.br e marketing.atitudereflexiva.com.br. Vamos configurar um virtual host para o alias financeiro que também permite acesso a nosso storage em /home/storage/www:

ProxyPassMatch ^/(artefato/.*)$ !

<VirtualHost  financeiro.atitudereflexiva.com.br:80>
   DocumentRoot /home/storage/www 
   ServerName  financeiro.atitudereflexiva.com.br
   ServerAlias  financeiro.atitudereflexiva.com.br
               
   Options FollowSymLinks Indexes MultiViews Includes

   <Directory /home/storage/www>
      Require all granted  
   </Directory>                                                
</VirtualHost>

<IfModule manager_module>
   Listen 8000                                      
   <VirtualHost *:8000>
      <Directory />
         # add ip of JBoss nodes to join this proxy here
         Require ip 127.0.0.1
      </Directory>

      <Location /mod_cluster_manager>
         SetHandler mod_cluster-manager
         Options +Indexes +Includes +FollowSymLinks +MultiViews
         AllowOverride All
      </Location>                                                      

      AdvertiseFrequency 5
      ServerAdvertise on
      EnableMCPMReceive
      AllowDisplay on
      KeepAliveTimeout 60

   </VirtualHost>               
</IfModule>

O ProxyPassMatch que configuramos não é necessário para o balancing, mas é interessante desviarmos o acesso de recursos estáticos que podem ser servidos pelo Apache sem necessidade de intermediação do JBoss. No meu caso, minha página carrega o CSS direto do storage:

/home/storage/www/artefatos/css/style.css

Minha página HTML acessa esse artefato assim:

<link type="text/css" rel="stylesheet" href="/artefatos/css/style.css"/>

Testando a Configuração

Suba o Apache e o JBoss. Se tudo estiver funcionando corretamente, você verá as informações abaixo ao acessar a URL:

http://127.0.0.1:8000/mod_cluster_manager

mod_cluster/1.3.1.Final

start of "httpd.conf" configuration
mod_proxy_cluster.c: OK
mod_sharedmem.c: OK
Protocol supported: AJP https
mod_advertise.c: OK
Server: localhost 
Server: localhost VirtualHost: *:8000 
Advertising on Group 224.0.1.105 
Port 23364 for (null)://(null):0 every 5 seconds
Server: financeiro.atitudereflexiva.com.br 
VirtualHost: financeiro.atitudereflexiva.com.br:80
end of "httpd.conf" configuration

Auto Refresh show DUMP output show INFO output

Node {HOST_NAME} (ajp://localhost:8009):

Enable Contexts Disable Contexts Stop Contexts
Balancer: mycluster,LBGroup: ,

Flushpackets: Off,Flushwait: 10000,Ping: 10000000,Smax: 151,Ttl: 60000000,
Status: OK,Elected: 70,Read: 61871,Transferred: 0,Connected: 0,Load: 100

Virtual Host 1:

Contexts:

/wildfly-services, Status: ENABLED Request: 0 Disable Stop
/, Status: ENABLED Request: 0 Disable Stop

Aliases:

localhost
default-host

Se você retirar a tag Location, ao acessar a URL da aplicação, você verá a página abaixo, pois o acesso estará sendo redirecionado direto para o storage (/home/storage/www/) e não para o JBoss:

http://financeiro.atitudereflexiva.com.br

Index of /

[ICO]   Name	        Last modified	  Size   Description

[   ]   artefato.zip   2018-11-21 09:52   10K	 
[DIR]   artefato/      2018-11-26 15:11   -	 

Apache/2.4.34 (Win32) OpenSSL/1.1.0i mod_cluster/1.3.1.Final PHP/7.2.9
Server at financeiro.atitudereflexiva.com.br Port 80

Referências

1. [https://kb.novaordis.com/index.php/Mod_cluster_Installation]
2. [http://www.howtobuildsoftware.com/index.php/how-do/btel/apache-wildfly-8-mod-cluster-wildfly-cluster-and-apache2]
3. [https://stackoverflow.com/questions/26440580/wildfly-8-mod-cluster-apache-integration]
4. [https://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypassmatch]
5. [https://docs.jboss.org/mod_cluster/1.1.0/html/native.config.html]
6. [http://localhost8080.blogspot.com/2016/09/load-balancer-com-wildfly-10.html]