ANEXO 15 ARQUITETURA DE REFERÊNCIA O CEPROFW é uma ferramenta que possui diversas funcionalidades desenvolvidas sobre a plataforma Java EE 6 tendo como objetivo prover um fluxo padrão para o desenvolvimento de aplicações. O CEPRO-FW tem como base a plataforma Java EE 6, utilizando como container oficial o JBoss AS 6. O JBoss provê uma série de serviços que são estendidos e abstraídos pelo framework, de forma a padronizar e facilitar o desenvolvimento de aplicações Java EE 6. Como parte principal da camada Web, o framework utiliza o JavaServer Faces na versão 2.1, integrado ainda ao conjunto de componentes PrimeFaces. Para javascript é feito uso intenso do jQuery. Na camada de serviço as regras de negócio são implementadas como EJBs, o que permite o uso do controle transacional e contexto de segurança do próprio container, utilizando JPA para o acesso a dados. O CEPRO-FW provê ainda o módulo CEPRO-FW Commmon que contém uma série de classes utilitárias úteis no desenvolvimento de aplicações Java em geral. Organização da Arquitetura: Como mostrado na figura acima, o framework utiliza o CDI como framework de injeção de dependências, servindo como "cola" entre todas as camadas e bibliotecas utilizadas. Diagramas de Classes: Página 1 de 15 Application Layers (FOWLER): As aplicações construídas com o framework serão construídas com camadas lógicas com o objetivo de aumentar a manutenibilidade, o reuso e a escalabilidade. Camadas: 1. Data source layer ou camada de acesso a dados 2. Domain model ou camada de lógica de negócio 3. Service layer ou façade 4. View ou Apresentação Model View Controller (FOWLER): Promove a separação de responsabilidades na construção da interface com usuário garantindo manutenibilidade e reuso de componentes de interface. Page Controller (FOWLER), View Helper (CORE): Ambos os padrões são semelhantes e podem ser aplicados como uma extensão do padrão model view controller. Como será utilizado JSF, existirá um managed bean para cada página, que realiza o papel de page controller. Service Layer (FOWLER), Session Façade (CORE), Façade (GAMMA): Estes padrões são aplicados nas classes da camada facade. Está classe tem como objetivo de simplificar a interface de negócio, expondo uma API simplificada para a camada de apresentação. Uma característica importante da camada façade é a ausência de manutenção de estado, facilitando sua exposição como um webservice, se for necessário. Página 2 de 15 Domain Model (FOWLER): Este padrão determina que todas as regras de negócios sejam implementadas em classes que não sejam acopladas com mecanismos de persistência ou de apresentação ao usuário. As classes de negócio serão implementadas conforme as diretivas deste padrão de Projeto Data Access Objects: Os Data Access Objects (DAO) são responsáveis por encapsular e abstrair o acesso a dados da aplicação. Uma aplicação deve conter um DAO para cada entidade ou grupo de entidades fortemente relacionadas, de forma a cobrir todo o modelo da mesma. Classes que executam regras de negócio devem obter os DAOs por injeção de dependência, usando a infraestrutura já disponibilizada pelo container (CDI). O CEPRO-FW provê uma classe abstrata chamada AbstractDAO que provê uma série de métodos comuns em todos os DAOs. Esta classe deve ser estendida na aplicação para gerar um BaseDAO para prover uma instância de Entity Manager, do qual todos os DAOs da aplicação devem estender. Visão Conceitual (modelo conceitual): Visão Lógica (modelo lógico): Página 3 de 15 Componentes: Plugin Gerador de Código Componentes de Infraestrutura Componentes de Negócio Componentes para Web Componente para Paginação sob Demanda Componente DynamicFilter Componente CKEditor Componente MailAsync Componente PersistenceManager Componente Menu Componente Menu Security Componente Agendador de Tarefas Componente para Certificação Digital Componente EntityConverter Componentes de Segurança Componentes de Cadastro Frameworks e Tecnologias: Página 4 de 15 Enterprise Java Beans (EJB 3): Especificação desenvolvida com intuito de prover um modo padrão para implementação de componentes de negócio, tipicamente encontrada em aplicações enterprise. Integrada com diversas tecnologias o framework gerencia integridade de dados, segurança, controle transacional, e de persistência de um modo padrão. JavaServer Faces (JSF 2.1): JSF é um framework web, criado para simplificar o desenvolvimento e integração de aplicações web. Atualmente é considerado um padrão da indústria, com suporte em todos os Application Server Java EE desde a versão 5. Spring Framework: Framework que provê uma série de funcionalidades necessárias para a maioria dos aplicativos Java tais como Inversão de Controle, Gerenciamento de Transações, Processamento Assíncrono, Processamento em Massa, Programação orientada a aspecto. PrimeFaces 2: Suíte de componentes para JSF 2, contendo mais de 100 componentes ricos com suporte a tecnologia web como AJAX, CSS 2 e 3, possibilidade de criação de temas, etc. Java Persistence API (JPA 2): API para gerenciamento de persistência. Gerencia a leitura e escrita de dados relacionais, criando uma camada de abstração acima do banco de dados relacional, diminuindo a complexidade de gerenciamento de cache, pool de conexões, transações, além de fazer com que a aplicação possa ser independente do banco de dados configurado. Dependency Injection for Java (JSR 330): Criado para padronizar as annotations necessárias para a implementação de Dependency Injection em aplicativos java, o que permite menor acoplamento entre as camadas, diminuindo consideravelmente a dependência entre as mesmas. Bean Validation(JSR 330): Framework para validação de Beans (entidades de negócio, objetos Java, etc). Criado para padronizar e simplificar a validação de dados, cuja fonte pode ser integração entre sistemas (Webservices), dados entrados pelo usuário via web, etc. Paginação Sob Demanda: Para implementar a paginação sob demanda utilizando o LazyObjectDataModel o desenvolvedor deve importar o LazyObjectDataModel e construir o objeto passando o Model que vai ser utilizado como Generic, passar o service que deve estender o PaginableService e um criteria que será passado do bean até o service como Objeto. Exemplo de utilização do LazyObjectDataModel: LazyObjectDataModel<TesteLazy>lazyList LazyObjectDataModel<TesteLazy>(testeLazyService,criteria); = new Como Funciona o LazyObjectDataModel: Quando o objeto é criado o PaginableService e o criteria são carregados, e em cada mudança de página também é chamado o metodoload. O no método load é passado a posição da pagina, a quantidade exibida por pagina, a coluna que será ordenada e a ordem, de acordo com essas informações são montadas a consulta para contar os registros totais e outra consulta que retorna apenas os registros que serão mostrados na tela. Página 5 de 15 Como Funciona o PaginableService: O PaginableService é uma classe Abstrata que estende o BaseService adicionando dois métodos essenciais para a construção de um LazyObjectDataModel, que são: FindAllByCriteriaPaginacao: Recebe como parâmetro a posição da pagina, a quantidade exibida por pagina, o criteria, a coluna que será ordenada e a ordem.Retorna uma lista genérica. CountAllByCriteria: Recebe como parâmetro a posição da pagina, a quantidade exibida por pagina, o criteria, a coluna que será ordenada e a ordem.Retorna um Long. Filtro Dinâmico: O objetivo deste texto é apresentar como é realizado a instalação dos filtros dinâmicos. Um filtro dinâmico é um componente visual destinado a permitir selecionar vários campos como critério de filtro. A utilização dos filtros dinâmicos é realizada em duas etapas. 1 – ETAPA: Consiste em anotar os campos do objetosCriteria, com a anota @FilterFieldOption, tais campos após anotados passarão a fazer parte da listagem de possíveis campos de filtragem. publicclassTesteLazyCriteriaimplementsDynamicQuery { private Date dataRealizacao; private Date dataVencimento; private String local; private String acao = "lista"; private String sortField; private String sortOrder; private List<FilterField>dynamicFilter; @FilterFieldOption(label = "Sigla", property = "sigTesteLazy", control=ControlType.COMBOBOX, valuesKeys="1, 2, 3", valuesString="BA, RJ, CE", value="BA", validator=SiglaValidator.class) private String sigla; @FilterFieldOption(label = "Descrição", property = "descTesteLazy") private String descricao; 2 – ETAPA: Consiste em adicionar a página XHTML o código necessário para geração do Filtro. <p:panelid="filtroPanel"toggleable="true"toggleSpeed="300" closeSpeed="2000"widgetVar="widgetFiltroPanel" styleClass="ui-kfe-panel-filtro"> <h:panelGridcolumns="2"styleClass="ui-kfe-component-panel"> <h:selectOneMenuvalue="#{TesteLazyBean.filterField}"> <f:selectItemsvalue="#{TesteLazyBean.filterFields}"/> </h:selectOneMenu> <p:commandButtonvalue="Adicionar" actionListener="#{TesteLazyBean.adicionar}" update="filtros, tabelaCampos"/> Página 6 de 15 <h:dataTableid="tabelaCampos" value="#{TesteLazyBean.criteria.dynamicFilter}"var="campo"> <p:columnheaderText="Campo"> <h:outputTextvalue="#{campo.field}"/> </p:column> <p:columnheaderText="Condição"> <h:selectOneMenuvalue="#{campo.expression}"> <f:selectItemsvalue="#{TesteLazyBean.expressions}"/> </h:selectOneMenu> </p:column> <p:columnheaderText="Campo"> <h:inputTextvalue="#{campo.value}" rendered="#{campo.controleq 'textbox'}"/> <h:selectOneMenuvalue="#{campo.value}" rendered="#{campo.controleq 'combobox'}"> <f:selectItemsvalue="#{campo.valuesString}"/> </h:selectOneMenu> </p:column> <p:columnheaderText=""id="remover"> <h:commandLinkvalue="Remover"> <p:collectorvalue="#{campo}"removeFrom="#{TesteLazyBean.criteria.dynamicFilter}"/> </h:commandLink> </p:column> </h:dataTable> </h:panelGrid> </p:panel> Página 7 de 15 Ceprofw – CKEditor: O Ceprofw-CKEditor é um componente que fornece um editor rico dentro da plataforma Ceprofw, este editor foi estendido da biblioteca Primefaces-Extension-CKEditor e foram adicionadas funcionalidades para atender às necessidades do CEPROMAT. Para usar o CKEditor basta realizar as seguintes configurações básicas no arquivo .XHTML: <ceprofw:ckEditor id="editor1" value="#{editorBean.htmlValue}" pathFile="img-ckeditor" imageLocalType="disk" fileUploadLimit="1000000" idDoc="#{editorBean.idDocumento}"> <p:ajax event="save" listener="#{editorBean.saveListener}" update="form"/> </ceprofw:ckEditor> Onde: id – é o id do componente; value – propriedade value padrão; pathFile – diretório aonde serão armazenadas as imagens no servidor; imageLocalType – nesta propriedade deverá ser informado os valores: disk ou db. Onde, disk será para guardar as imagens no disco do servidor dentro de pathFile ou db que irá direcionar as imagens para o banco de dados; fileUploadLimit – tamanho máximo do arquivo de imagem; idDoc – valor da propriedade id do objeto aonde será guardado o texto do editor. Este id será um subtiretório de pathFile. Ex.: img-ckeditor/1020. Ou seja, para cada idDoc um subdiretório será criado dentro do caminho informado em pathFile. Observação: Neste caso, para que as imagens sejam gravadas corretamente no disco, faz-se necessário que o objeto que possui o idDoc já esteja persistido no banco de dados. Exemplo simples de utilização do Ceprofw-CKEditor: Guardando as Imagens no disco. //Criando o ManegedBean publicclassEditorBeanextendsBaseBean { @EJB privateDocumentoServicedocumentoService; private Documento doc = null; private Long idDocumento; private String descricao; private String htmlValue; private String textValue; private Long idLocate = 0l; privatefinal String PATH_FILE = "img-ckeditor"; //Getters e Setters omitidos publicString novo(){ //Regra para limpar campos returnnull; Página 8 de 15 } public String pesquisar(){ doc = documentoService.findId(idLocate); if (doc!=null){ idDocumento = doc.getId(); textValue = doc.getValorTexto(); } returnnull; } public String delete(){ String path = FacesContext.getCurrentInstance(). getExternalContext().getRealPath("")+ "\\" + PATH_FILE + "\\" + doc.getId(); if (documentoService.delete(doc)) deleteDir(path); returnnull; } public String salvar(){ if (idDocumento==null) doc = newDocumento(); else doc = documentoService.findId(idDocumento); doc.setDescricao(descricao); doc.setValorHtml(htmlValue); documentoService.persist(doc); idDocumento = doc.getId(); returnnull; } public void saveListener() { salvar(); } } Página 9 de 15 Guardando as imagens no banco de dados: Para guardar as imagens no banco de dados deve-se criar uma classe de serviço implementando a interface IAnexoService do Ceprofw. Em seguida, implementar os métodos save e findId. No arquivo .XHTML informar o nome completo da classe criada para a propriedade classService do CKEditor. Veja a baixo um exemplo de utilização; Classe de serviço: importbr.gov.mt.cepromat.ceprofw.core.service.editor.IAnexoService; publicclassUserAnexoServiceimplementsIAnexoService{ privateIAnexoServiceanexoService; privatevoidserviceLookup() throwsNamingException{ InitialContextctxIni = newInitialContext(); anexoService = (IAnexoService) ctxIni.lookup("java:global/exemploapp/exemplo-web-0.0.1-SNAPSHOT/AnexoService"); } @Override public String save(byte[] image, String fileName, Long idDocumento) { String id_imagem=null; try { serviceLookup(); id_imagem = anexoService.save(image, fileName, idDocumento); } catch (NamingException e) { e.printStackTrace(); } returnid_imagem; } @Override publicbyte[] findId(Long id) { byte[] bytes = null; try { serviceLookup(); bytes = anexoService.findId(id); } catch (NamingException e) { e.printStackTrace(); } return bytes; } } Configurando as propriedades no arquivo .XHTML: Página 10 de 15 <ceprofw:ckEditor id="editor1" value="#{editorBean.htmlValue}" imageLocalType="db" fileUploadLimit="1000000" classService="br.gov.mt.cepromat.exemplo.web.service.UserAnexoService" idDoc="#{editorBean.idDocumento}"/> Observação: O Ceprofw guardará as imagens enviadas dentro do diretório de backup tmp-ceprofw-img com a extensão .TMP. Configurando Vários Editores na tela: Para que a aplicação possa utilizar mais de um editor na tela deve-se criar uma classe de serviço (IAnexoService) para cada editor. Como no exemplo acima, esta classe será responsável em gerenciar as imagens no banco de dados. <h:panelGridcolumns="1"> <ceprofw:ckEditor id="editor1" width="0" value="#{editorBean.htmlValue}" imageLocalType="db" fileUploadLimit="1000000" classService="br.gov.mt.cepromat.exemplo.web.service.UserAnexoService" idDoc="#{editorBean.idDocumento}"> <p:ajax event="save" listener="#{editorBean.saveListener}" update="form"/> </ceprofw:ckEditor> <ceprofw:ckEditor id="editor2" width="0" interfaceColor="#6495ED" value="#{editorBean.htmlProcessoValue}" imageLocalType="db" fileUploadLimit="1000000" classService="br.gov.mt.cepromat.exemplo.web.service.UserAnexo2Service" idDoc="#{editorBean.idDocumento}"> <p:ajax event="save" listener="#{editorBean.saveListener}" update="form"/> </ceprofw:ckEditor> </h:panelGrid> Observação: caso a propriedade idDoc for diferente para cada editor, faz-se necessária a mudança do arquivo de configuração do CKEditor. Convertendo o Texto formato Html para Texto simples: Para converter o texto Html para o formato texto simples basta utilizar a classe Html2Text do Ceprofw, esta classe possui um método estático html2text que recebe como parâmetro uma string contendo o texto em formatoHtml e retorna uma string com um texto sem as tags HTML. Ex.: Página 11 de 15 Importbr.gov.mt.cepromat.ceprofw.web.jsf.component.editor.Html2Text; … Html2Text.html2text(textoHtml); … Ceprofw –EntityConverter: O EntityConverter é um conversor genérico para entidades JPA, seguindo sua especificação. A conversão não limita apenas a essas entidades, sendo possível converter outros objetos, onde seja criado algum identificador e seu atributo/método seja anotado com @ConverterId. É importante destacar que para seu funcionamento a classe deve possuir seu devido método equals sendo feito pelo identificador da entidade. Abaixo um exemplo de sua utilização: <h:selectOneMenu value="#{bean.group}" converter="entityConverter" > <f:selectItems value="#{bean.groups}" var="group" itemLabel="#{group.description}" /> </h:selectOneMenu> O funcionamento desse conversor se dá da seguinte maneira, cada objeto da lista é adicionado ao ViewMap do JSF, com isso ao submeter a página o objeto é recuperado desse Map. Isso torna desnecessário consultas extras no banco de dados ou a criação de um conversor para cada entidade Configurando o JBoss para Usar o JAAS Arquivo de Configuração: Para configurar API JAAS no jboss o desenvolvedor precisa acessar o arquivo de configuração (standalone.xml/domain.xml) e adicionar em <security-domains> a seguinte parametrização. . . . <security-domain name="jaasMenu" cache-type="default"> <authentication> <login-module code="Database" flag="required"> <module-option name="dsJndiName" value="java:jboss/datasources/exemplo_local"/> <module-option name="principalsQuery" value="select pws from user where name=?"/> <module-option name="rolesQuery" value="select ur.name, 'Roles' from user_role ur inner join user_user_role uur on (ur.id = uur.roles_id) inner join user u on (u.id = uur.user_id) where u.name =?"/> <module-option name="unauthenticatedIdentity" value="guest"/> </login-module> </authentication> </security-domain> . . . dsJndiName – Deve ser definido o banco de dados padrão para acesso aos dados de usuário, senha e regras de acesso neste exemplo foi utilizado o endereço JNDI “java:jboss/datasources/exemplo_local”. principalsQuery – Neste parâmetro deve ser definido a query principal para validar a senha do usuário. rolesQuery – Esta query serve para que o JAAS localize as regras de acesso para o usuário. Página 12 de 15 Este é um exemplo simples com os parâmetros básicos de autenticação. Existem outros parâmetros como: hashAlgorithm: define o algoritmo de criptografia da senha; hashEncoding: formato do texto da senha. Configurar jboss-web.xml: Adicionar um arquivo de configuração do JBoss (jboss-web.xml) copiar para o mesmo local do web.xml, com o conteúdo abaixo: <?xml version="1.0" encoding="UTF-8"?> <jboss-web> <security-domain>java:/jaas/jaasMenu</security-domain> </jboss-web> Configurar web.xml: <!-- Protected Areas --> <security-constraint> <web-resource-collection> <web-resource-name>Páginas protegidas</web-resource-name> <url-pattern>/resources/pages/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>ADM</role-name> <role-name>USER</role-name> </auth-constraint> </security-constraint> <!-- Validation By Form --> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/exemplo/login</form-login-page> <form-error-page>/resources/pages/common/error.jsf</form-error-page> </form-login-config> </login-config> <!-- Allowed Roles --> <security-role> <role-name>ADM</role-name> </security-role> <security-role> <role-name>USER</role-name> </security-role> Tabelas no banco de dados: CREATE TABLE tb_teste.user ( id bigint NOT NULL, "name" character varying(30), pws character varying(30), CONSTRAINT user_pkey PRIMARY KEY (id) Página 13 de 15 ); create table tb_teste.role( id bigint primary key not null, name varchar(30) not null ); CREATE TABLE tb_teste.user_role ( user_id bigint NOT NULL, role_id bigint NOT NULL ) ALTER TABLE user_role ADD PRIMARY KEY(role_id, user_id); ALTER TABLE user_role ADD FOREIGN KEY (user_id) REFERENCES user(id); ALTER TABLE user_role ADD FOREIGN KEY (role_id) REFERENCES role(id); Inserindo Usuário: INSERT INTO "user"(id, pws, "name") VALUES(1,'1','cepro'); Inserindo Regras: Com a tabela de regras criada, inserir dados de exemplo. INSERT INTO role(id,name) VALUES (1,'USR'); INSERT INTO role(id,name) VALUES (2, 'ADM'); Inserindo Regras do Usuário: INSERT INTO user_role(user_id,role_id) VALUES(1,1) Formulário de Login: Este é um exemplo simples de um formulário de login, deve-se observar que a action do form j_security_check é padrão da API JAAS, assim como o nome dos campos: j_username e j_password. <form method="post" action="j_security_check"> <h:panelGrid columns="2"> <h:outputLabel for="j_username" value="Username" /> <input type="text" id="j_username" name="j_username" /> <h:outputLabel for="j_password" value="Password" /> <input type="password" id="j_password" name="j_password" /> <input type="submit" name="submit" value="Login" /> </h:panelGrid> </form> Arquitetura Específica da Secretaria de Estado de Segurança Pública: Características Arquiteturais: 1. A utilização da especificação JAVA EE 6 (EJB, JSF, JPA,CDI); 2. Controle transacional por parte do servidor de aplicação (Especificação EJB Java EE 6); 3. Servidor de Aplicação Weblogic 12.1.3 (Fornece as bibliotecas para suporte ao JEE 6); 4. framework SHIRO 1.3.2 para segurança de acesso; 5. framework DeltaSpike 1.5.0 para suporte à especificação CDI; Página 14 de 15 6. Archetype Maven 1.0 visando uma padronização estrutural dos novos projetos. Referências: http://docs.huihoo.com/jboss/portal/2.6/reference-guide/html/authentication.html http://amatya.net/blog/implementing-security-with-jaas-in-jboss-as-7/ http://www.j3ltd.com/articles/jaas/Jaas1.htm Página 15 de 15