Deployment of a Spring Boot App with Jenkins in a Unix System using Gradle

One thing that I found very hard to do was to integrate a spring boot project into a CI environment using jenkins. As a default behavior, the Jenkins process tree killer always kills the process started by a job which stops the execution of a Spring Boot App after the jenkins job finishes. In addition of that, I wanted to see the server log on the jenkyns windows until it finishes loading. This article will try to help us solving this problems.

But first I would like to discuss what I consider a good practice to a Spring Boot App CI environment. I find very useful to first copy the artifacts to a specified area on the server to keep track of the artifacts deployed and deploy the artifact from that location. Also, I create a server log file there and start to listening on the jenkins window until the server started.

So the script below does that. With some minor improvements self explained on the comments, but in summary it does this:

  • stop whatever process running on the deployed port.
  • delete the files of the previous deploy
  • copy the files to deploy location
  • start application with nohup command, java – jar
  • start listening to the server log until it reaches an specific instruction.

Finally you have to do some adjustments to your job on Jenkins to avoid the default tree killing process. Just add this instruction before calling the sh: BUILD_ID=dontKillMe /path/to/my/script.sh (FIGURE 3)

You can see the jenkins job configuration window on FIGURES 1, 2, and 3 and the log result window on FIGURES 4 and 5.

Go to my github repo to check the project, but it recommend to extract the shell script to another repo to keep it lifecycle independent of your app.

https://github.com/rcoli/spring-boot-jenkins

Compilação de múltiplos projetos Maven

Em projetos de desenvolvimento de software mais complexos não é muito incomum a necessidade de quebrarmos uma aplicação em pedaços menores que poderão ser reutilizados por outros projetos. E assim, acabamos criando a necessidade da compilação de múltiplos projetos de uma vez e em uma determinada ordem.

O Maven nos ajuda muito na tarefa de gestão de dependências e para o build de múltiplos projetos ao mesmo tempo eles devem ter uma relação modular ou uma relação de pai para filho, saiba mais aqui e isto nem sempre é desejado num ambiente de integração contínua.

O generic-maven-build nada mais é do que um script preparado para compilação de múltiplos projetos de uma só vez, validando se compilaram corretamente e exibindo o tempo decorrido ao final.

A instrução executada partiu de uma necessidade de um projeto atual,  mvn clean install -U, no entanto nada impede que você faça a alteração no arquivo de acordo com a sua necessidade.

Explicando um pouco melhor as intruções contidas no arquivo:

  • clean:  attempts to clean a project’s working directory of the files that we’re generated at build-time. By default, it discovers and deletes the directories configured in project.build.directory, project.build.outputDirectory, project.build.testOutputDirectory, and project.reporting.outputDirectory.

  • install: install the package into the local repository, for use as a dependency in other projects locally

  • -U: Force Update, atualiza todas as dependências a partir dos repositórios configurados no pom.xml ou settings.xml

Instruções de uso

Baixe o arquivo no meu repositório do bitbuket em uma pasta local do seu computador: https://rcoli@bitbucket.org/rcoli/generic-maven-build.git

Ali existem as versões para Windows (pouco evoluída, fique a vontade para melhorá-la) e linux.

Para executá-lo, acesso o terminal ou prompt de comando e digite da seguinte forma:

Executando sem testes: generic-maven-build.sh /home/user/repository/project1/pom.xml /home/user/repository/project1/pom.xml semTestes

Executando com testes: generic-maven-build.sh /home/user/repository/project1/pom.xml /home/user/repository/project1/pom.xml

No Eclipse:

Clique no ícone External Tools na barra de tarefas do eclipse, escolha External Tools Configurations. Clique com o botão direito em Program e escolha new. Configure conforme as imagens abaixo:

Screenshot from 2015-07-08 10:26:07

Screenshot from 2015-07-08 10:26:28

Screenshot from 2015-07-08 10:28:35

Screenshot from 2015-07-08 10:28:48

Screenshot from 2015-07-08 10:37:19

Pré-requisitos:

Maven instalado e configurado, arquivo .sh ou .bat baixado em seu computador.

Referências:

https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

http://maven.apache.org/plugins/maven-clean-plugin/

http://books.sonatype.com/mvnex-book/reference/multimodule-web-spring-sect-intro.html

Cálculo Simples de Data sem o uso de Date ou Calendar

Uma das grandes dificuldades do Java é trabalhar com datas. Existem diversas APIs que resolvem esse problema de forma completa. No entanto através de um simples cálculo utilizando os operadores “/” (divisão) e “%” (resto), somos capazes de obter o resultado sem a utilização de nenhuma API específica. Obviamente trabalhando com String.

Saída:
Hoje é: dia 3, mês 4, ano 14.

Hoje é: dia 23, mês 4, ano 2014.

[php]
public class Main {

public static void main(String[] args) {

String hojeComAnoEmDoisDigitos = "030414";
System.out.println(escreverDataPorExtensoComAnoEmDoisDigitos(hojeComAnoEmDoisDigitos));

String hojeComAnoEmQuatroDigitos = "23042014";
System.out.println(escreverDataPorExtensoComAnoEmQuatroDigitos(hojeComAnoEmQuatroDigitos));
}

public static String escreverDataPorExtensoComAnoEmDoisDigitos(String hojeComAnoEmDoisDigitos) {
try {

if(!isDataValida(hojeComAnoEmDoisDigitos, 6))
return ("Data Inválida, Verifique se o dia, o mês e ano possuem 2 dígitos.");

Integer divisorDiaDoisDigitos = 10_000;
Integer divisorAnoDoisDigitos = 100;

Integer hoje = Integer.parseInt(hojeComAnoEmDoisDigitos);

Integer dia;
Integer mes;
Integer ano;

dia = (hoje / divisorDiaDoisDigitos);
mes = (hoje / divisorAnoDoisDigitos) % 100;
ano = hoje % divisorAnoDoisDigitos;

return String.format("Hoje é: dia %d, mês %d, ano %d. \n", dia, mes, ano);

} catch (NumberFormatException e) {
return "Erro ao formatar valor para número.";
}
}

public static String escreverDataPorExtensoComAnoEmQuatroDigitos(String hojeComAnoEmQuatroDigitos) {
try {

if(!isDataValida(hojeComAnoEmQuatroDigitos, 8))
return ("Data Inválida, Verifique se o dia, o mês possuem 2 dígitos e o ano quatro.");

Integer hoje = Integer.parseInt(hojeComAnoEmQuatroDigitos);

Integer divisorDiaQuatroDigitos = 1_000_000;
Integer divisorAnoQuatroDigitos = 10_000;

Integer dia;
Integer mes;
Integer ano;

dia = (hoje / divisorDiaQuatroDigitos);
mes = (hoje / divisorAnoQuatroDigitos) % 100;
ano = hoje % divisorAnoQuatroDigitos;

return String.format("Hoje é: dia %d, mês %d, ano %d. \n", dia, mes, ano);

} catch (NumberFormatException e) {
return "Erro ao formatar valor para número.";
}
}

private static boolean isDataValida(String hoje, int i) {
return hoje.length() == i;
}

}[/php]

Back-up rápido de tabelas MySQL

Uma boa prática  antes de atualizar a sua base é sempre fazer um back-up das tabelas que pode ser feito de forma rápida com o seguinte comando:
[sourcecode language=”html”]
// criando a nova tabela
CREATE TABLE nova_tabela LIKE minha_base.tabela;
// copiando os dados

INSERT nova_tabela SELECT * FROM minha_base.tabela;
[/sourcecode]

CHAR ou VARCHAR? Que tipo de campo escolher no MySQL?

Ambos os tipos de campo “texto” podem ser definidos com um comprimento máximo no MySQL. A principal diferença entre os dois tipos de campos é que o CHAR sempre armazenará o texto com o tamanho fixo, mesmo que o “texto” seja menor do que o tamanho máximo especificado na criação do campo. Neste caso, o próprio SGBD do MySQL irá usar espaços a direita do “texto” preenchendo o campo até a quantidade máxima especificada em sua criação. Note, que esses espaços serão removidos quando você recuperar os dados a armazenados.

Por outro lado, “textos” armazenadas em uma coluna VARCHAR exigem apenas o tamanho do “texto” armazenado. Portanto, a palavra “ivo” em uma coluna VARCHAR(10) irá requerer quatro bytes de espaço (o comprimento do “texto” mais 1), enquanto em uma coluna CHAR(10), a mesma palavra exigirá 10 bytes de espaço.

Assim, de modo geral, as colunas VARCHAR tendem a consumir menos espaço em disco do que colunas CHAR. Contudo, as bases de dados são normalmente mais rápidas quando se trabalha com colunas de tamanho fixo, o que é um argumento a favor de CHAR. E essa mesma palavra de três letras, “ivo”, em um CHAR(3) utiliza apenas 3 bytes, enquanto em um VARCHAR(10) requer 4. Desta forma, como decidir qual usar?

Se o “texto” for sempre de um tamanho determinado (por exemplo, uma abreviatura do estado), use CHAR, caso contrário, use VARCHAR. Você pode notar, porém, que, em alguns casos, o MySQL define uma coluna de um tipo (como CHAR) embora você a tenha criado como outro tipo (VARCHAR). Isto é perfeitamente normal e é a maneira que o MySQL possui para melhorar o desempenho.

OBS: Note que a palavra texto encontra-se marcada aqui no sentido de ressaltar que o campo “string” pode, além de conter caracteres, conter também números, datas ou listas armazenadas como texto (string). Neste caso, não poderão ser usadas as funções SQL de cálculo específicas de outros tipos. A não ser que sejam convertidas antes.

Fonte: PHP 6 and MySQL 5 for Dynamic Web Sites. ULLMAN, Larry, 2007.

Função Veja Mais/Saiba Mais em PHP

Neste tutorial apresento uma simples função em PHP que retornará uma string com o tamanho especifico seguida do link “veja mais” que redirecionará para a URL. Função bem simples que é muito utilizadas em layouts que não permitem que o conteúdo se expanda muito sem perder a formatação.

A função recebe 4 parâmetros, a frase que deverá ser encurtada ($string); o tamanho de caracteres que deverá ter a string final ($corte), com um valor padrão; o link ($url) e o texto do link, também com um valor padrão: “Veja Mais”.

Note que a frase será cortada no espaço imediatamente anterior ao tamanho especificado.

[php]
// Retorna a string com o tamanho específico e o link veja mais

function vejaMais($string, $tamanho = 10, $url, $txt="Veja Mais"){

$tamanhoStr = strlen($string);

for ($index = 0; $index < $tamanhoStr; $index++) {

$caractere = $string[$index];

if ($caractere == " ") {

if ($index <= $tamanho) {
$corte = $index;
}
}
}

return substr($string, 0, $corte) . "… <a href=\"$url\">" . $txt . "</a>";
}
[/php]

Abaixo, como chamar o código na página. A recomendação é que seja usado o primeiro método evitando que o nome seja confundido com as funções reservadas do PHP. Neste caso, não faz muita diferença uma vez que não há funções em português em PHP.

[php]
// 1 exemplo de chamada

echo call_user_func_array(‘vejaMais’,array(‘Minha String a ser cortada’, 50, ‘http://www.google.com’));

// OU simplesmente

echo vejaMais(‘Minha String a ser cortada’, 50, ‘http://www.google.com’);

[/php]

Essa instrução retornará a seguinte frase: Minha String a ser… Veja Mais

Tipos de Campos no MySQL, saiba como escolher o tipo correto

Este post tem o objetivo de ajudar o desenvolvedor na escolha adequada do tipo de campo que deverá utilizar no banco MySQL para persistir seus dados.

O desenvolvedor inexperiente costuma confundir bastante os tipos de campo da linguagem utilizada (PHP por exemplo) com os tipos que o banco pode armazenar. Um exemplo clássico dessa confusão é o booleano que é automaticamente convertido de true para 1 e false para 0 (zero).  Além disso, há o clássico erro de armazenamento de CPF em campo numérico o que faz com que todos os zeros a esquerda se percam.

Portanto coloco abaixo a tabela com os campos do MySQL para que sirva de um guia de implementação do banco de dados de sua aplicação.

Lembro ainda que antes de mais nada você deve identificar qual o tipo de variável que irá armazenar: número, texto, binário, data,  lista ou combinação destes. Em seguida estimar o tamanho do campo e se será variável ou fixo. O MySQL costuma preencher campos texto não variáveis com espaços em branco. Em seguida, verificar se permitirá nulo ou não, se é uma chave candidata, primária, estrangeira, índice ou único.

Não há uma regra fixa para saber como os bancos deverão ser implementados, mas tenha sempre em mente que neste caso, mais é melhor. Ou seja, é melhor sobrar espaço do que ter uma informação inserida com pedaços faltando. Já viu alguém nascer no “Rio de Jane” pois o desenvolvedor não colocou o campo com o tamanho correto?!

Observações:

  • O tamanho do campo para tipos numéricos não afeta o intervalo de valores que pode ser armazenado na coluna. Colunas definidas como TINYINT (1) ou TINYINT (20) podem armazenar exatamente, os mesmos valores. Em vez disso, para inteiros, o tamanho determina a largura do campo, para casas decimais, o tamanho é o número total de dígitos que pode ser armazenado.
  • Muitos dos tipos de dados têm nomes sinônimos: INT e INTEGER, DEC e DECIMAL, etc
  • O tipo de campo TIMESTAMP é automaticamente definido com a data e hora quando um INSERT ou UPDATE ocorre, mesmo se nenhum valor for especificado para esse campo . Se uma tabela possui múltiplas colunas TIMESTAMP, somente a primeira será atualizada quando um INSERT ou UPDATE é realizado.
  • MySQL também tem diversas variantes para os tipos de texto que permitem o armazenamento de dados binários. Estes tipos são BINARY, VARBINARY, TINYBLOB, MEDIUMBLOB, e LONGBLOB. Esses tipos são usados para armazenamento de arquivos ou dados criptografados.
Tipos Numéricos
Tipo Uso Tamanho
Atributo MIN MAX
TINYINT Um inteiro muito pequeno Signed: -128 127
Unsigned 0 255
SMALLINT Um inteiro pequeno Signed: –32768 32767
Unsigned 0 65535
MEDIUMINT Um inteiro de tamanho mediano Signed: –8388608 8388607
Unsigned 0 16777215
INT or INTEGER Um inteiro de tamanho normal Signed: –2147483648 2147483647
Unsigned 0 4294967295
BIGINT Um inteiro de temanho grande Signed: –9223372036854775808 9223372036854775807
Unsigned 0 18446744073709551615
FLOAT Um pequeno número de ponto flutuante (precisão simples) Signed –3.402823466E+38 –1.175494351E-38, 0
1.175494351E-38 3.402823466E+38
Não pode ser unsigned
OBS Se o número de decimais não for especificado ou for <= 24 será de precisão simples
DOUBLE,
DOUBLE PRECISION,
REAL
Um número de ponto flutuante de tamanho normal (precisão dupla) Signed -1.7976931348623157E+308 -2.2250738585072014E-308, 0
2.2250738585072014E-308 1.7976931348623157E+308
Não pode ser unsigned
OBS Se o número de decimais não for especificado ou for 25 <= Decimals <= 53 será de precisão dupla
DECIMAL,
NUMERIC
Um número de ponto flutuante descompactado . Signed Se comporta como um campo CHAR: “descompactado” significa que o número é armazenado como uma string, usando um caractere para cada dígito do valor. O ponto decimal e, para números negativos, o sinal ‘-‘ não é contado. Se o decimal for 0, os valores não terão ponto decimal ou parte fracionária. O alcance máximo de valores decimais é o mesmo que para o DOUBLE, mas a faixa atual para um campo DECIMAL dado pode ser limitado pela escolha de comprimento e decimais.
Não pode ser unsigned
OBS Se Decimais é deixado de fora ele é definido como 0. Se o comprimento é deixado de fora ele é definido como 10. Note que no MySQL 3,22 o comprimento inclui o sinal eo ponto decimal
Campos de Datas
Formato MIN MAX
DATE Data ‘1000-01-01’ ‘9999-12-31’
OBS Formato: ‘YYYY-MM-DD’
DATETIME Data e horário ‘1000-01-01 00:00:00’ ‘9999-12-31 23:59:59’
OBS Formato: ‘YYYY-MM-DD HH:MM:SS’
TIMESTAMP Timestamp ‘1970-01-01 00:00:00’ aproximadamente 2037
OBS Formato: YYYYMMDDHHMMSS, YYMMDDHHMMSS, YYYYMMDD ou YYMMDD, dependendo se M é 14 (ausente), 12, 8 ou 6, podendo ser strings ou números.

Este tipo é recomendável para instruções de INSERT ou UPDATE pois é automaticamente marcado com os valores da operação mais recente quando não informado.

TIME A time ‘-838:59:59’ ‘838:59:59’
OBS formato: ‘HH:MM:SS’, podem ser strings ou números
YEAR Anos com 2 ou 4 digitos. O padrão é 4 digitos 4 digitos 1901 2155 e 0000
2 digitos 1970 2069
OBS Formato: YYYY
podem ser strings ou números.
Campos Texto
MIN MAX
CHAR String de tamanho fixo. Sempre é completada com espaços a direita até o tamanho definido 1 255 caracteres
OBS Espaços excessivos são removidos quando o valor é trazido.Os valores são ordenados e comparados ignorando caixas altas e baixas de acordo com a codificação padrão, a menos que seja fornecido uma chave binária.
VARCHAR String de tamanho variável 1 255 caracteres
OBS Os valores são ordenados e comparados ignorando caixas altas e baixas de acordo com a codificação padrão, a menos que seja fornecido uma chave binária.Nota: Espaços execessivos são removidos quando o valor é inserido.
TINYTEXT 0 255 (2^8 – 1) caracteres
TEXT 0 65535 (2^16 – 1) caracteres
MEDIUMTEXT 0 16777215 (2^24 – 1) caracteres
LONGTEXT 0 4294967295 (2^32 – 1) caracteres
Dados Binários
TINYBLOB 0 255 (2^8 – 1) caracteres
BLOB 0 65535 (2^16 – 1) caracteres
MEDIUMBLOB 0 16777215 (2^24 – 1) caracteres
LONGBLOB 0 4294967295 (2^32 – 1) caracteres
Listas
MIN MAX
ENUM Enumeração String que pode conter apenas um valor ou zero 65535 valores distintos.
SET Lista String que pode conter zero ou mais valores 64 itens
fonte: http://help.scibit.com/Mascon/masconMySQL_Field_Types.html e http://dev.mysql.com/doc/refman/5.0/en/data-type-overview.html(tradução livre)

Nota:

JQuery e AJAX – Combo de Estado e Cidade

Essa semana precisei implementar uma solução de combo dinâmico de Estado / Cidade que carregasse as opções das cidades de acordo com a escolha do estado. Obviamente teria que usar Ajax (JQuery) e o Json (JavaScript Object Notation) que é um formato leve de troca de dados (fonte: http://www.json.org/).

Fiz uma pesquisa rápida na NET e de cara encontrei um excelente artigo de Davi Ferreira: “Populando selects de cidades e estados com AJAX (PHP e jQuery)” (http://www.daviferreira.com/posts/populando-selects-de-cidades-e-estados-com-ajax-php-e-jquery). No entanto para o meu caso a solução do Davi teria que ser reinventada tendo em vista que no meu form haviam 3 conjuntos de Estado/Cidade. Um para os dados de endereço do usuário e outros dois para os dados profissionais. Uma opção seria repetir o código 3 vezes, uma para cada conjunto, o que não é nada elegante.

A opção foi portanto, criar uma função dentro do Javascript que fizesse o trabalho sujo de forma dinâmica. Ao invés de criar uma função JS que aproveitasse o JQuery, segui a sugestão do Basil Godman, “Definindo suas próprias funções com o JQuery” (http://blogs.microsoft.co.il/blogs/basil/archive/2008/09/22/defining-your-own-functions-in-jquery.aspx) e criei uma função do próprio JQuery.

Outra coisa que fiz também foi retirar o campo de “span” estático com a mensagem “Carregando …” como sugere o Davi. Ela agora aparece de forma dinâmica dentro da própria função. Facilitando a manutenção, inclusão de em gif animado, uma classe específica e despoluindo o HTML, imagina isso para 4 ou 5 campos Estado/Cidade!

Abaixo você poderá baixar o código completo inclusive com o DDL e o DML do MySQL, além de verificar os 4 arquivos necessários para que tudo funcione comentados.

Arquivos completos para download

Arquivo de conexão com o Banco
[php]$con = mysql_connect( ‘localhost’, ‘root’, ” ) ;
mysql_select_db( ‘cadastro’, $con );[/php]

O PHP e o HTML dos Selects
[sourcecode language=”html”]<?php
require("conn.open.php");

function listaEstadosOrderIdAsc(){
return mysql_query("SELECT cod_estados, sigla FROM estados ORDER BY sigla ASC");
}
?>
<p>
<label for="cod_estados">Estado:</label>
<select name="cod_estados" id="cod_estados">
<option value="">(selecione aqui)</option>
<?php $estados = listaEstadosOrderIdAsc(); while ($row = mysql_fetch_object($estados)) { ?>
<option value="<?php echo $row->cod_estados; ?>"><?php echo $row->sigla; ?></option>
<?php } ?>
</select>
<label for="cod_cidades">Cidade:</label>
<select name="cod_cidades" id="cod_cidades">
<option value="">– Escolha um estado –</option>
</select>
</p>
[/sourcecode]

Aqui a função JQuery propriamente dita, comentada para facilitar o entendimento.

[sourcecode language=”javascript”]
$(document).ready(function(){

// Por Rogerio Coli, www.rcoli.com.br Рfavor ṇo remover
jQuery.fn.carregaCidades = function() {

// Objeto que guarda os argumentos
var args = arguments[0] || {};

//id do Select de Cidades
var idSelectCidade = args.idSelectCidade;

// Página que irá criar o JSon
var paginaPhpCidades = ‘cidades.ajax.php’;

// Conteúdo do elemento span que vai aparecer enquanto carregam as cidades,
// pode ser substituído por uma imagem. Coloque a tag completa
var carregandoMsg = ‘Aguarde, carregando…’

// Classe do elemento span que vai aparecer enquanto carregam as cidades
var carregandoClass = ‘class’;
// após as cidades carregarem aparece esta mensagem
var jsonPrimeiroElemento = ‘(selecione a cidade)’;
// Aqui eu pego a frase do primeiro option de Cidade
var primeiroElemento = $(idSelectCidade).find(‘option:first’).html();

if( $(this).val() ) {
// escondendo as cidades até carregarem
$(idSelectCidade).hide();
// mensagem de espera: carregando
$(idSelectCidade).after(‘<span class=’+ carregandoClass +’>’+carregandoMsg+'</span>’);

$.getJSON(paginaPhpCidades+’?search=’,{cod_estados: $(this).val(), ajax: ‘true’}, function(j){
// É importante que o value seja vazio pra que o formulário não seja enviado vazio
// caso use o form validate
var options = ‘<option value="">’+jsonPrimeiroElemento+'</option>’;
for (var i = 0; i < j.length; i++) {
// É importante que o value seja vazio pra que o formulário não seja enviado vazio
// caso use o form validate
options += ‘<option value="’ + j[i].cod_cidades + ‘">’ + j[i].nome + ‘</option>’;
}
// mostrando as cidades após carregarem e removendo a mensagem de espera
$(idSelectCidade).html(options).show();
$(idSelectCidade).next().remove();
});
} else {
$(idSelectCidade).html(‘<option value="">’+primeiroElemento+'</option>’);
}

};
//Inciando o SELECT, importante ao recarregar a página
$("#cod_estados option:first").attr(‘selected’,’selected’);
// Aqui eu chamo a função e o método que irá carregá-la
$(‘#cod_estados’).change(function(){ $(this).carregaCidades({idSelectCidade: ‘#cod_cidades’}); })
});
[/sourcecode]

O Arquivo AJAX que monta o JSon

[sourcecode language=”php”]
header( ‘Cache-Control: no-cache’ );
header( ‘Content-type: application/xml; charset="utf-8"’, true );

require("conn.open.php");

$cod_estados = mysql_real_escape_string( $_REQUEST[‘cod_estados’] );

$cidades = array();

$sql = "SELECT cod_cidades, nome
FROM cidades
WHERE estados_cod_estados=$cod_estados
ORDER BY nome";
$res = mysql_query( $sql );
while ( $row = mysql_fetch_assoc( $res ) ) {
$cidades[] = array(
‘cod_cidades’ => $row[‘cod_cidades’],
‘nome’ => htmlentities($row[‘nome’]),
);
}

echo( json_encode( $cidades ) );

[/sourcecode]

JQuery Form Validation РValida̤̣o de Datas no Google Chrome

Recentemente me deparei com um BUG seríssimo do plugin do JQuery, form validation. O campo “data:true” do jQuery Validation Plugin 1.9.0 aceita o formato de data em português (ex: 31/12/2011) em todos os navegadores exceto o Google Chrome (15.0.874.121 m, no momento desse artigo) e possivelmente no Safari, de acordo com algumas reclamações de outros programadores com o mesmo problema.

Ao tentar buscar alternativas de contornar o problema eu percebi que havia uma funcionalidade já desenvolvida para a validação de data no formato DE (alemão,”29.04.1994″ ou “1.1.2006”), bastante semelhante ao padrão brasileiro. Só que essa função não aparece no arquivo disponível para o download, ou foi retirada posteriormente como pode ser visto no repositório do GitHub do plugin. Sendo que na linha 275 (messages) alguns arquivos do site do plugin ainda fazem referência a tal função: “dateDE”.

Para normalizar a situação eu sugiro incluir algumas linhas de código no seu “jquery.validate.js”, vamos a elas:

Procure nas linhas 268/275 dentro de messages aonde aparece a mensagem de data e coloque abaixo de date a linha destacada:
[sourcecode language=”javascript” highlight=”8″]
messages: {
required: "Campo obrigat&oacute;rio.",
remote: "Por favor revise este campo.",
email: "Por favor digite um e-mail v&aacute;lido.",
url: "Por favor digite uma URL v&aacute;lida.",
date: "Por favor digite uma data v&aacute;lida.",
dateISO: "Por favor digite uma data v&aacute;lida (ISO).",
dateDE: "Por favor digite uma data v&aacute;lida (DE, BR).",
number: "Por favor digite um número v&aacute;lido.",
digits: "Por favor digite somente números.",
creditcard: "Por favor digite um cartão de crédito v&aacute;lido.",
equalTo: "Por favor digite o mesmo valor.",
accept: "Por favor digite um valor com uma extensão v&aacute;lida.",
maxlength: $.validator.format("Por favor digite mais de {0} caracteres."),
minlength: $.validator.format("Por favor digite pelo menos {0} caracteres."),
rangelength:$.validator.format("Por favor digite um valor entre {0} e {1} caracteres."),
range: $.validator.format("Por favor digite um valor entre {0} e {1}."),
max: $.validator.format("Por favor digite um valor menor ou igual a {0}."),
min: $.validator.format("Por favor digite um valor maior ou igual a {0}.")
},
[/sourcecode]

Em seguida ache a função dateISO lá pela linha 1050 e insira a a função abaixo. Atente que há a função original e a alterada
[sourcecode language=”javascript”]
// //http://docs.jquery.com/Plugins/Validation/Methods/dateDE
dateDE:function(value,element){
// Original DE
//return this.optional(element)||/^\d\d?\.\d\d?\.\d\d\d?\d?$/.test(value);
// Oposite ISO
return this.optional(element)||/^\d{1,2}[\/-]\d{1,2}[\/-]\d{4}$/.test(value);
},
[/sourcecode]
Agora é só fazer a chamada normalmente em sua validação:
[sourcecode language=”javascript”]
$("#form").validate({
rules:
{
data_nascimento: { required:true, minlength: 10, dateDE:true },
}
});
[/sourcecode]

Atente que esta validação não verifica se é uma data válida, somente se atende a um formato específico. Para a validação de uma data válida (meses com 30 ou 31 dias, anos bissextos e etc) é necessário
extender as funcionalidades do plugin. Em breve farei isso, até lá.

O que é Spring Web Flow?

Neste tutorial apresento uma série de perguntas e respostas para quem estiver interessado em saber um pouco mais sobre o Spring Web Flow e suas aplicações.

O que é Spring Web Flow?
Spring Web Flow é uma extensão do Spring MVC que permite a implementação do fluxo de navegação de uma aplicação web.

O que é fluxo de navegação?
Um fluxo encapsula uma seqüência de etapas que guiam o usuário através da execução de algumas tarefas de negócios. Ele abrange várias solicitações HTTP, estados, dados transacionais, é reutilizável e pode ser dinâmico e de longa duração.

Quando devo usar o Spring Web Flow?
Quando sua aplicação necessitar de uma navegação controlada. Por exemplo, o check-in para um voo, a solicitação de um empréstimo, carrinho de compras, ou mesmo adicionando um passo de confirmação para um formulário. O que esses cenários têm em comum é uma ou mais das seguintes características:

  1. Há um início claro e um ponto final.
  2. O usuário deve passar por um conjunto de telas em uma ordem específica.
  3. As alterações não são finalizados até a última etapa.
  4. Uma vez completo, não deve ser possível repetir uma transação acidentalmente.

Em que mais o Spring Web Flow pode ajudar?
Pode ajudar em aplicações onde:

  1. Visualizar o fluxo é muito difícil.
  2. A aplicação tem um monte de código de acesso a sessão HTTP.
  3. Forçar a navegação controlada é importante, mas não possível.
  4. O suporte adequado ao botão Voltar do navegador parece inatingível.
  5. O navegador e o servidor ficam fora de sincronia com o uso do botão “Voltar”.
  6. Várias abas do navegador causam problemas de concorrência com os dados da sessão HTTP.

Posso Integrar o Spring Web Flow com outras Tecnologias?
Certamente, Spring Web Flow fornece uma linguagem declarativa de definição de fluxo para os fluxos de criação em um nível maior de abstração. Ele permite que ele seja integrado em uma grande variedade de aplicações sem quaisquer alterações (para o modelo de programação de fluxo), incluindo Spring MVC, JSF, e até mesmo aplicações Portlet web.

fonte: http://www.springsource.org/spring-web-flow#documentation. Acessado em 14/11/2011.

Assine o RSS de meus Artigos e Comentários.