Estava tendo problemas com a versão 340.96 do driver da NVidia quando compilava o kernel 4.6.4, no começo pensei ser problema de configuração no kernel, pois fazia anos que não fazia isso :), mas depois de algum tempo de pesquisa descobri que era um bug no driver da NVidia mesmo.
Então vamos lá:
(Vou mostrar como fazer a instalação do zero, mas se você já tinha o driver funcionando, mas só quer saber como faze-lo funcionar no novo kernel, pode pular esta parte.)
Antes de tudo, remova tudo que você tinha instalado da nvidia:
baixe o patch do driver da nvidia: https://git.archlinux.org/svntogit/packages.git/plain/trunk/linux-4.6.patch?h=packages/nvidia-340xx
salve em algum diretório como linux-4.6.patch
Reinicie o computador, na tela de login digite Ctrl+Alt+F1, vai entrar no terminal do linux, faça o login, entre no diretorio onde se encontra o driver e digite:
sudo chmod +x NVIDIA-Linux-x86_64-340.96.run
Pare o X-Server
sudo service lightdm stop
Agora vamos começar a brincadeira com a versão do driver 340.96 da NVidia.
Como falei, tem um bug nele que impede de compilar no 4.6.*
Extraia o driver da NVidia
sudo ./NVIDIA-Linux-x86_64-340.96.run -x
Entre no diretorio que extraimos:
cd NVIDIA-Linux-x86_64-340.96
copie o arquivo linux-4.6.patch que baixamos para o diretório NVIDIA-Linux-x86_64-340.96
Aplique o patch:
patch -p1 < linux-4.6.patch
entre no diretório kernel (dentro de NVIDIA-Linux-x86_64-340.96) e digite:
make clean
make install
Após a compilação volte um nível (NVIDIA-Linux-x86_64-340.96) e digite:
./nvidia-installer
Para mim, a opção para registrar o modulo DKMS não funcionou, marquei como ‘No’
Terminado a instalação, reiniciei o computador e o driver estava configurado.
Foi nítida a diferença de qualidade de gráfico do driver nouveau para o driver proprietário da NVidia.
Uma dúvida comum de quem vai instalar o Redmine pela primeira vez no Debian é, qual tutorial seguir, tendo em vista que na internet existem vários tutoriais. Mas qual usar? Pensando nisso, fiz um passo a passo rápido e facil, para instalar o Redmine no Debian.
Primeiro de tudo, vamos atualizar a lista de aplicativos no repositório com apt-get update.
sudo apt-get update
Após isso, vamos instalar o MySQL (pode usar o PostgreSQL se preferir)
Instalar o MySQL
sudo apt-get install mysql-server
Ao instalar o MySQL, será pedido uma senha de root
Uma tarefa muito comum, porém chata é a leitura de templates do Microsoft Word e a alteração de palavras chaves personalizadas por um valor em específico. Há diversas formas de se fazer isso, hoje vou mostrar uma dessas formas, utilizando o Apache POI.
Este código foi testado com as versões 3.10-FINAL e 3.12 do Apache POI. Devido a um Bug na versão 3.12 é necessário adicionar a biblioteca poi-scratchpad as dependências, caso contrário você receberá o erro:
error: cannot access Paragraph
List<XWPFRun> runs = paragraph.getRuns();
^
class file for org.apache.poi.wp.usermodel.Paragraph not found
Nas dependências do gradle adicione as seguintes bibliotecas:
Vamos utilizar um modelo simples (o código do exemplo é para arquivos .docx), mas bastante comum. Um texto com uma tabela estática, para efeito de exemplo coloquei apenas duas variáveis no documento, #valor_parcela# e uma variável na tabela #valor_total#.
Veja a imagem do modelo, abaixo:
E o código?
Vamos lá, chega de enrolação e vamos ao código.
Criei um método extractTemplate(InputStream stream, Map<String, String> properties) que recebe um stream do documento e um Map<String, String> com as chaves/valores que iremos substituir.
Você pode fazer o download do código fonte do exemplo aqui. (https://github.com/cadocruz/exemplo-apache-poi)
Abaixo dois métodos extras, um para configurar o tamanho da tabela e outro para configurar um paragrafo.
Muitas vezes quando estamos desenvolvendo com Oracle ADF no JDeveloper, e estamos criando nossas views, o JDeveloper faz o binding automatico dos componentes da nossa view com nosso Manage Bean, e dependendo da complexidade da view o manage bean vai ficar entulhado de código inútil.
Mas há 2 formas de desativá-los para a view desejada.
Configuração no JDeveloper
Manual
Configuração no JDeveloper
Vamos ver como fazer pela própria configuração do JDeveloper.
Abrir a pagina jsf no modo Design.
Ir no Menu Design
Abrir aba Component Binding e desmarcar a opção Auto Bind
Clique OK.
Manual
O modo manual é muito mais rápido e facil, é só procurar pelo seguinte comentário no fonte da view:
Hoje vou falar um pouco do JHipster ou “Java Hipster” (como os criadores costumam chamar), é um gerador do Yeoman para criação de aplicações modernas em Java, com Spring Boot e AngularJS. Ele possibilita a utilização de tecnologias como Spring Boot, Spring MVC REST, JPA, segurança com Spring Security e bancos de dados como MySQL, PostgreSQL, MongoDB e Cassandra, entre outras.
Em pouco tempo, o JHipster vem se tornando muito popular no Github, e tem sido destaque de revistas online como InfoQ, Infoworld e SD Times, e também em conferências em Paris, Londres, Montreal, Omaha, Taipei , Richmond e Frankfurt.
Instalação
Vamos lá, para instalar o JHipster você precisa do Java instalado, Git e Node.js (para usar o gerenciador de pacotes do node, npm).
Instalar o Yeoman: npm install -g yo
Instalar o Bower: npm install -g bower
Instalar o Grunt: npm install -g grunt-cli, se preferir pode instalar o Gulp: npm install -g gulp.
E finalmente instalar o JHipster: npm install -g generator-jhipster
Criando um projeto
Crie um diretório onde ficará o projeto, e dentro do diretório que você acabou de criar, digite o comando yo jhipster.
Após isso é só ir respondendo as questões relativas ao seu projeto. Dependendo da sua escolha, as perguntas podem mudar.
? (1/13) What is the base name of your application? mywebapp
Aqui informo o nome da aplicação
? (2/13) What is your default Java package name? br.com.cadocruz
O nome do pacote padrão no Java
? (3/13) Do you want to use Java 8? Yes (use Java 8)
Aqui podemos escolher entre usar o Java 8 ou Java 7
? (4/13) Which *type* of authentication would you like to use? HTTP Session Authentication (stateful, default Spring Security mechanism)
O tipo de autenticação que vamos usar, as opções são: HTTP Session Authentication, OAuth2 Authentication e Token-based authentication
? (5/13) Which *type* of database would you like to use? SQL (H2, MySQL, PostgreSQL)
O tipo de banco de dados vamos usar: SQL (H2, MySQL, PostgreSQL), MongoDB ou Cassandra
? (6/13) Which *production* database would you like to use? MySQL
Como escolhi SQL na pergunta anterior, aqui escolho qual banco usar em produção MySQL ou PostgreSQL
? (7/13) Which *development* database would you like to use? H2 in-memory with Web console
Aqui semelhante a pergunta acima, mas para o ambiente de desenvolvimento: Aqui inclui a possibilidade de usar o H2
? (8/13) Do you want to use Hibernate 2nd level cache? Yes, with ehcache (local cache, for a single node)
Escolho se desejo usar o segundo nível de cache do Hibernate, temos 3 opções: Não, Sim com ehcache (cache local) ou com HazelCast (cache distribuído)
? (9/13) Do you want to use clustered HTTP sessions? No
Aqui seleciono se desejo usar cluster para as sessões com o HazelCast
? (10/13) Do you want to use WebSockets? No
Se quero usar WebSockets
? (11/13) Would you like to use Maven or Gradle for building the backend? Maven (recommended)
Escolho entre Maven ou Gradle
? (12/13) Would you like to use Grunt or Gulp.js for building the frontend? Grunt (recommended)
Semelhante a escolha acima, esta é para build de front-end.
? (13/13) Would you like to use the Compass CSS Authoring Framework? No
E a última pergunta é se queremos usar Compass
Após as escolhas, o JHipster irá baixar todos os arquivos necessários para nossa aplicação (este processo pode demorar alguns minutos). Para rodar a aplicação, simplesmente digitamos: mvn spring-boot:run
A aplicação estará disponível em http://localhost:8080
Página inicial
Monitoramento
Gerenciadmento de logs
Internacionalização
O JHipster possui suporte a vários idiomas, por padrão vem instalado apenas inglês e francês. Mas para instalarmos português é simples, digitamos: yo jhipster:languages, procuramos pressionando a seta para baixo ou para cima e selecionamos com a tecla de espaço “Portuguese (Brazilian)”
Languages configuration is starting
? Please choose additional languages to install: (Press <space> to select)
❯◯ Catalan
◯ Chinese (Simplified)
◯ Chinese (Traditional)
◯ Danish
◯ German
◯ Korean
◯ Hungarian
(Move up and down to reveal more choices)
Entidades
Também podemos adicionar outras entitidades. Digamos que queremos criar uma entidade Autor e Livro, para isso precisariamos criar para cada entidade:
Uma tabela no banco
Uma JPA Entity
Um Spring Data JPA Repository
Um Controller com Spring MVC REST, com as operaçoes basicas de CRUD
Um AngularJS router, um controller e um service
Uma página HTML
Se necessitarmos criar relacionamentos, isso se torna ainda pior. Com o JHipster esta tarefa é muito fácil, digitamos no console yo jhipster:entity autor e respondemos algumas questões:
The entity autor is being created.
Generating field #1
? Do you want to add a field to your entity? Yes
? What is the name of your field? nome
? What is the type of your field? String
===========Autor==============
nome (String)
Generating field #2
? Do you want to add a field to your entity? Yes
? What is the name of your field? dataNascimento
? What is the type of your field? DateTime
===========Autor==============
nome (String)
dataNascimento (DateTime)
Generating field #3
? Do you want to add a field to your entity? No
===========Autor==============
nome (String)
dataNascimento (DateTime)
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Livro
? What is the name of the relationship? livro
? What is the type of the relationship? one-to-many
===========Autor==============
nome (String)
dataNascimento (DateTime)-------------------
livros - livro (one-to-many)
Generating relationships with other entities
? Do you want to add a relationship to another entity? No
===========Autor==============
nome (String)
dataNascimento (DateTime)-------------------
livro - livro (one-to-many)
? Do you want pagination on your entity? Yes, with a simple pager
Everything is configured, generating the entity...
create .jhipster/Autor.json
create src/main/java/br/com/cadocruz/domain/Autor.java
create src/main/java/br/com/cadocruz/repository/AutorRepository.java
create src/main/java/br/com/cadocruz/web/rest/AutorResource.java
create src/main/resources/config/liquibase/changelog/20150306164732_added_entity_Autor.xml
create src/main/webapp/scripts/app/entities/autor/autors.html
create src/main/webapp/scripts/app/entities/autor/autor-detail.html
create src/main/webapp/scripts/app/entities/autor/autor.js
create src/main/webapp/scripts/app/entities/autor/autor.controller.js
create src/main/webapp/scripts/app/entities/autor/autor-detail.controller.js
create src/main/webapp/scripts/components/entities/autor/autor.service.js
create src/test/java/br/com/cadocruz/web/rest/AutorResourceTest.java
create src/test/gatling/simulations/AutorGatlingTest.scala
create src/main/webapp/i18n/en/autor.json
create src/main/webapp/i18n/fr/autor.json
create src/main/webapp/i18n/pt-br/autor.json
Repare que o JHipster já criou todos os arquivos necessários, nos poupando muito trabalho. Mas ainda precisamos criar a entidade Livro: yo jhipster:entity livro
The entity livro is being created.
Generating field #1
? Do you want to add a field to your entity? Yes
? What is the name of your field? titulo
? What is the type of your field? String
===========Livro==============
titulo (String)
Generating field #2
? Do you want to add a field to your entity? Yes
? What is the name of your field? dataPublicacao
? What is the type of your field? DateTime
===========Livro==============
titulo (String)
dataPublicacao (DateTime)
Generating field #3
? Do you want to add a field to your entity? No
===========Livro==============
titulo (String)
dataPublicacao (DateTime)
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? autor
? What is the name of the relationship? autor
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'autor'do you want to use? nome
===========Livro==============
titulo (String)
dataPublicacao (DateTime)-------------------
autor - autor (many-to-one)
Generating relationships with other entities
? Do you want to add a relationship to another entity? No
===========Livro==============
titulo (String)
dataPublicacao (DateTime)-------------------
autor - autor (many-to-one)
? Do you want pagination on your entity? No
Everything is configured, generating the entity...
create .jhipster/Livro.json
create src/main/java/br/com/cadocruz/domain/Livro.java
create src/main/java/br/com/cadocruz/repository/LivroRepository.java
create src/main/java/br/com/cadocruz/web/rest/LivroResource.java
create src/main/resources/config/liquibase/changelog/20150306170841_added_entity_Livro.xml
create src/main/webapp/scripts/app/entities/livro/livros.html
create src/main/webapp/scripts/app/entities/livro/livro-detail.html
create src/main/webapp/scripts/app/entities/livro/livro.js
create src/main/webapp/scripts/app/entities/livro/livro.controller.js
create src/main/webapp/scripts/app/entities/livro/livro-detail.controller.js
create src/main/webapp/scripts/components/entities/livro/livro.service.js
create src/test/java/br/com/cadocruz/web/rest/LivroResourceTest.java
create src/test/gatling/simulations/LivroGatlingTest.scala
create src/main/webapp/i18n/en/livro.json
create src/main/webapp/i18n/fr/livro.json
create src/main/webapp/i18n/pt-br/livro.json
Subindo novamente a aplicação podemos ver que nossas entidades foram criadas com as operações básicas de CRUD.
Services
Caso queira criar Services (para sua regra de negócio) o JHipster também tem um comando específico para isso: yo jhipster:service [nome da service].
Conclusão
Apesar de ser um projeto novo, o JHipster possui várias funcionalidades interessantes que facilitam a criação de Web Apps usando Spring Boot e AngularJS.
Enquanto eu escrevia este post, saiu a nova versão (2.5.2) do JHipster, com várias correções de bugs, confira o Release notes
Para utilizarmos a atenticação Digest devemos saber como ele funciona. Ao contrário
da autenticação Basic, a autenticação Digest não envia a senha em texto puro pelo meio.
A Autenticação Digest é um mecanismo de autenticação simples desenvolvido originalmente para o protocolo HTTP
que está descrito na RFC2671.
O cliente envia um requisição à um conteúdo, que requer autenticação, sem fornecer usuário e senha. Ex.:
GET /api/hello HTTP/1.1
Host: localhost:8080
Neste caso acessamos o conteúdo (path) /api/hello pelo método GET. O servidor responde com 401 Unauthorized
e gera um desafio digest e o envia para o cliente.
Não tenho a pretensão de ser um guia definitivo sobre autenticação Digest, por isso não vou explicar cada parâmetro
enviado no HEADER. Esses parâmetros são usados na resposta ao desafio. (Para mais informações sobre os
parâmetros acessar o site RFC2671 ).
O nonce é uma string gerada pelo servidor toda vez que lança um desafio. Deve ser única, ou seja, não deve ser
repetido. No Spring Security o nonce é gerado da seguinte maneira:
O Spring Boot é um projeto com o conceito de “convenção sobre configuração”, tornando a configuração do
ambiente muito mais rápida e simples.
Você pode aprender um pouco mais sobre o Spring Boot na página de referência do próprio
Spring Boot.
Um jeito simples de criar um projeto do Spring Boot é pelo site Spring Initializr,
é só você configurar como será sua aplicação e as depêndencias dela, e gerar o projeto.
Estou usando o Gradle ao invés do Maven, mas você pode usar o maven se quiser.
O arquivo build.gradle da nossa aplicação ficou assim:
Pronto, já temos nossa aplicação rodando com o mínimo de segurança.
Spring Security
Diferentemente da autenticação Basic, na autenticação Digest o Spring Security não provê
uma configuração “automática”, então teremos que definir manualmente nossos DigestAuthenticationEntryPoint
e DigestAuthenticationFilter.
Desabilitamos a proteção contra CSRF (Cross-site Request Forgery) para simplificar o desenvolvimento,
mas você pode ler mais sobre proteção CSRF aqui.
No método digestEntryPoint() configuramos nossa chave privada, o tempo de expiração do nonce em segundos (o valor padrão é 300 segundos) e o nosso realm. Esses são parâmetros obrigatórios.
Vamos agora para nosso @RestController, ele não tem nada de mais. Mapeamos nosso controller para /api e nosso método
hello como /hello.
A primeira resposta do servidor ainda é 401 Unauthorized, mas o desafio agora é interpretado e enviado em um segundo request, que irá ter sucesso com 200 OK:
O cliente pode enviar o Authorization header já no primeiro request, e evitar o desafio de segurança do
segundo request.
Agora que já temos nosso serviço RESTful funcionando, vamos criar nossa página de login (index.html) que consumirá nosso serviço usando a autenticação Digest.
Nada de mais na nossa página, um campo para usuário e outro para senha. O serviço “sujo” deve ser feito por um
javascript fazendo requisição por AJAX. Nós mesmo podemos criar nosso javascript, mas com certeza alguém já fez.
Pesquisando “ajax digest auth” no google, o terceiro resultado é do Marcin Michalski ‘s Weblog, pegarei este como exemplo:
/*
* A JavaScript implementation of the Digest Authentication
* Digest Authentication, as defined in RFC 2617.
* Version 1.0 Copyright (C) Maricn Michalski (https://marcin-michalski.pl)
* Distributed under the BSD License
*
* site: https://arrowgroup.eu
*/$.Class("pl.arrowgroup.DigestAuthentication",{MAX_ATTEMPTS:1,AUTHORIZATION_HEADER:"Authorization",WWW_AUTHENTICATE_HEADER:'WWW-Authenticate',NC:"00000001",//currently nc value is fixed it is not incrementedHTTP_METHOD:"GET",/**
* settings json:
* - onSuccess - on success callback
* - onFailure - on failure callback
* - username - user name
* - password - user password
* - cnonce - client nonce
*/init:function(settings){this.settings=settings;},setCredentials:function(username,password){this.settings.username=username;this.settings.password=password;},call:function(uri){this.attempts=0;this.invokeCall(uri);},invokeCall:function(uri,authorizationHeader){vardigestAuth=this;$.ajax({url:uri,type:this.HTTP_METHOD,beforeSend:function(request){if(typeofauthorizationHeader!='undefined'){request.setRequestHeader(digestAuth.AUTHORIZATION_HEADER,authorizationHeader);}},success:function(response){digestAuth.settings.onSuccess(response);},error:function(response){if(digestAuth.attempts==digestAuth.MAX_ATTEMPTS){digestAuth.settings.onFailure(response);return;}varparamParser=newpl.arrowgroup.HeaderParamsParser(response.getResponseHeader(digestAuth.WWW_AUTHENTICATE_HEADER));varnonce=paramParser.getParam("nonce");varrealm=paramParser.getParam("realm");varqop=paramParser.getParam("qop");varresponse=digestAuth.calculateResponse(uri,nonce,realm,qop);varauthorizationHeaderValue=digestAuth.generateAuthorizationHeader(paramParser.headerValue,response,uri);digestAuth.attempts++;digestAuth.invokeCall(uri,authorizationHeaderValue);}});},calculateResponse:function(uri,nonce,realm,qop){vara2=this.HTTP_METHOD+":"+uri;vara2Md5=hex_md5(a2);vara1Md5=hex_md5(this.settings.username+":"+realm+":"+this.settings.password);vardigest=a1Md5+":"+nonce+":"+this.NC+":"+this.settings.cnonce+":"+qop+":"+a2Md5;returnhex_md5(digest);},generateAuthorizationHeader:function(wwwAuthenticationHeader,response,uri){returnwwwAuthenticationHeader+', username="'+this.settings.username+'", uri="'+uri+'", response="'+response+'", nc='+this.NC+', cnonce="'+this.settings.cnonce+'"';}});$.Class("pl.arrowgroup.HeaderParamsParser",{init:function(headerValue){this.headerValue=headerValue;this.headerParams=this.headerValue.split(",");},getParam:function(paramName){varparamVal=null;$.each(this.headerParams,function(index,value){if(value.indexOf(paramName)>0){paramVal=value.split(paramName+"=")[1];paramVal=paramVal.substring(1,paramVal.length-1);}});returnparamVal;}});
Testando nossa aplicação podemos perceber que recebemos a caixa de diálogo do login, pedindo usuário e senha.
Como na imagem abaixo:
Como contornar isso?
No mesmo post do Marcin Michalski ‘s Weblog ele da uma solução para isso não ocorrer. Estender a classe DigestAuthenticationEntryPoint, o que ele faz é alterar o código de status 401 Unauthorized para 403 Forbidden.
Após muita pesquisa para descobrir a melhor forma de contornar este problema, cheguei a uma solução, alterar o HTTP Authentication Scheme. Quando enviamos o HEADER para o cliente, enviamos o tipo de autenticação (Authentication Scheme) WWW-Authenticate: Digest, para que o browser não mostre a caixa de diálogo do login, criaremos nosso próprio Authentication Scheme.
O que precisamos fazer é estender a classe DigestAuthenticationEntryPoint e criarmos nossa própria implementação do Digest. Na verdade usaremos o mesmo código do DigestAuthenticationEntryPoint, mudando apenas o Authentication Scheme de Digest para outro de nossa escolha, usarei DigestCustom.
Dei o nome da classe de RestDigestAuthenticationEntryPoint, veja abaixo como ficou o código:
publicclassRestDigestAuthenticationEntryPointextendsDigestAuthenticationEntryPoint{privatestaticfinalLoglogger=LogFactory.getLog(RestDigestAuthenticationEntryPoint.class);publicvoidcommence(HttpServletRequestrequest,HttpServletResponseresponse,AuthenticationExceptionauthException)throwsIOException,ServletException{HttpServletResponsehttpResponse=(HttpServletResponse)response;// compute a nonce (do not use remote IP address due to proxy farms)// format of nonce is:// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))longexpiryTime=System.currentTimeMillis()+(super.getNonceValiditySeconds()*1000);StringsignatureValue=DigestUtils.md5DigestAsHex(newString(expiryTime+":"+super.getKey()).getBytes());StringnonceValue=expiryTime+":"+signatureValue;StringnonceValueBase64=newString(Base64.encode(nonceValue.getBytes()));// qop is quality of protection, as defined by RFC 2617.// we do not use opaque due to IE violation of RFC 2617 in not// representing opaque on subsequent requests in same session.StringauthenticateHeader="DigestCustom realm=\""+super.getRealmName()+"\", "+"qop=\"auth\", nonce=\""+nonceValueBase64+"\"";if(authExceptioninstanceofNonceExpiredException){authenticateHeader=authenticateHeader+", stale=\"true\"";}if(logger.isDebugEnabled()){logger.debug("WWW-Authenticate header sent to user agent: "+authenticateHeader);}httpResponse.addHeader("WWW-Authenticate",authenticateHeader);httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage());}}
Devemos mudar nossa classe SecurityConfig e trocar a classe DigestAuthenticationEntryPoint pela RestDigestAuthenticationEntryPoint
Também devemos mudar o javascript digest-auth.js, pois o Spring Security não aceitará nosso DigestCustom sem termos que implementar nosso próprio filter, mas não queremos isso, devemos enviar no HEADER, Authorization: Digest.