Geração de compromissos com Java usando o padrão iCalendar

Propaganda
: : www.mundoj.com.br : :
Geração de compromissos
com Java usando o padrão iCalendar
Décio Heinzelmann Luckow
([email protected]): é bacharel em Sistemas
de Informação pela Univille e pós-graduando
em Gestão de Projetos pela Sustentare Escola de
Negócios. Já trabalhou com as linguagens ASP, PHP,
Python e tem se especializado nas tecnologias Java,
atuando no desenvolvimento Web e integração de
sistemas. Possui as certificações Java SCJP 1.5 e
SCWCD. Atua como analista de sistemas na TOTVS,
na área de Inteligência Empresarial. É autor do livro
Programação Java para a Web da editora Novatec,
além de artigos pela revista MundoJ.
Aprenda a gerar compromissos via Java e enviá-los
para programas como Google Calendar e Mozilla
Sunbird.
Este artigo apresentará o padrão iCalendar,
que é o formato para envio de agendamentos
utilizado em ferramentas de calendário como
Google Calendar e Mozilla Sunbird, entre
outras. Entenderemos as principais opções do
formato iCalendar, como enviar agendamentos
via e-mail com Java e como criar calendários
disponíveis na internet.
geração de agendamentos é algo que pode ser muito
bem explorado em diversos tipos de aplicativos. Como,
por exemplo, podemos citar: aviso de pagamentos de
contas, agendamento de consultas médicas ou aviso de eventos.
Utilizando o padrão iCalendar é possível enviar estes agendamentos por e-mail ou criar um serviço de calendário na internet e
sempre manter a sua agenda atualizada. Este é o formato adotado
por programas como Google Calendar e Mozilla Sunbird, o que
garante um grande campo de adoção deste recurso. Neste artigo
aprenderemos a enviar e-mails em Java no formato iCalendar e
como criar um serviço de calendário na internet.
O formato iCalendar
O formato iCalendar foi criado pelo Internet Engineering Task
Force no Calendaring and Scheduling Working Group em 1998.
A última atualização do formato ocorreu em setembro de 2009.
Este formato tem como objetivo definir agendamentos que po-
32
dem ser enviados via arquivo, via e-mail ou compartilhados
na internet. Um exemplo básico de um agendamento pode ser
conferido na Listagem 1.
Listagem 1. Exemplo de agendamento simples no formato
iCalendar.
BEGIN:VCALENDAR
VERSION:2.0
METHOD:REQUEST
PRODID:-//Revista MundoJ//Teste do artigo //PT
BEGIN:VEVENT
UID:evento001-javaparaweb.com.br
DTSTART:20110415T183000
DTEND:20110415T203000
SUMMARY:Ler a revista MundoJ
END:VEVENT
END:VCALENDAR
Este exemplo cria um agendamento com o assunto “Ler revista MundoJ” que ocorrerá entre as 18h30 e 20h30 do dia
15/04/2011. Um objeto iCalendar sempre irá começar com
BEGIN:VCALENDAR e finalizar com END:VCALENDAR. Este
mesmo padrão de BEGIN e END também é adotado por outros
agrupadores de informações do agendamento, que oficialmente
são chamados de componentes de calendário. O exemplo da Listagem 1 possui o VEVENT que descreve o evento em si. Porém,
além deste, ainda existem também os componentes VTODO,
VJOURNAL, VFREEBUSY, VTIMEZONE e VALARM.
De todos estes componentes apenas o VCALENDAR, VEVENT e
VALARM são implementados pela maioria dos aplicativos de calendário. O VALARM permite configurar como deverá funcionar o
aviso do compromisso, com ele você pode definir que 15 minutos
antes do compromisso, deve exibir uma janela de aviso, ou tocar
um determinado som.
Este trecho de código que define o agendamento pode ser disponibilizado de três formas básicas: por um arquivo com a extensão .ics, via e-mail ou via serviço de calendário. Porém, antes
de explorarmos as várias formas existentes para se compartilhar
um agendamento, vamos estudar um exemplo mais completo e
conhecer a finalidade de cada parâmetro do iCalendar.
Opções do VCALENDAR
O VCALENDAR é o elemento principal de um iCalendar, suas
principais propriedades são:
t 7&34*0/o&TQFDJmDBBWFSTÍPEBFTQFDJmDBÎÍPEPJ$BMFOEBS
a versão atual é a 2.0. A informação é obrigatória.
t .&5)0% o &TQFDJmDB RVF UJQP EF USBOTBÎÍP P BSRVJWP PV
e-mail do agendamento irá realizar. Para o envio por e-mail
é obrigatório que se tenha um METHOD especificado também no Content-Type da mensagem de e-mail. Estes dois METHOD devem sempre ter o mesmo valor (veremos mais detalhes sobre isso ao fazer o envio do agendamento por e-mail).
t 130%*%o²BJEFOUJmDBÎÍPEPQSPEVUPRVFHFSPVPBHFOEBmento. Não existe um formato obrigatório, mas recomenda-se
usar o seguinte padrão:
PRODID:-//Nome da Empresa//Nome do Aplicativo//Idioma
Opções do VEVENT
O VEVENT especifica o evento em si, este deverá estar dentro de
um VCALENDAR e só poderá ter um VALARM dentro dele. Suas
principais propriedades são:
t $"5&(03*&4 o &TQFDJmDB VNB DBUFHPSJB QBSB P BHFOEBmento. Alguns dos valores padrões são: ANNIVERSARY,
APPOINTMENT, BUSINESS, EDUCATION, HOLIDAY, MEETING, NON-WORKING HOURS, NOT IN OFFICE, PERSONAL, PHONE CALL, TRAVEL, VACATION.
t $-"44 o &TQFDJmDB VNB DMBTTJmDBÎÍP EF BDFTTP BP BHFOEBmento, por parte de outros usuários. Os valores possíveis são:
PUBLIC, PRIVATE e CONFIDENTIAL.
t $0..&/5 o 6N UFYUP MJWSF DPN VN DPNFOUÈSJP TPCSF P
agendamento.
t 46.."3:o0BTTVOUPEPBHFOEBNFOUP
t %&4$3*15*0/ o 6NB EFTDSJÎÍP QBSB P BHFOEBNFOUP NBJT
completa que o SUMMARY.
t (&0o*OGPSNBVNBQPTJÎÍPHMPCBMQBSBBBUJWJEBEFBHFOEBEB
é composta pela latitude e longitude, respectivamente. ExemQMP (&0 .VTFV EF "SUF EF 4ÍP
Paulo).
t -0$"5*0/o6NUFYUPMJWSFTPCSFPMPDBMEPFWFOUP&YFNplo: LOCATION: Sala de reunião 12, terceiro andar.
t 13*03*5:o&TQFDJmDBBQSJPSJEBEFEPBHFOEBNFOUPFNRVF
0 é o mais prioritário e 9 é o menos prioritário.
t 3&4063$&4 o 6NB MJTUB TFQBSBEB QPS WÓSHVMB EPT FRVJQBmentos necessários para o evento. Exemplo: PROJETOR, AUDIO, TELEFONE.
t 45"564o&TQFDJmDBPTUBUVTEPFWFOUPQPEFOEPTFSVNEPT
valores: TENTATIVE, CONFIRMED e CANCELLED.
t %545"35 o &TQFDJmDB B EBUBIPSB EF JOÓDJP EP FWFOUP "Tsim como todas as propriedades de data, deve ser descrito no
formato: AAAAMMDD’T’HHMMSS ou algumas variações para
data e hora absoluta e com fuso horário, conforme os exemplos:
DTSTART:20110415T133000 (hora local)
DTSTART: 20110415T173000Z (hora absoluta, ou a hora no
Meridiano de Greenwich)
%545"355;*%"NFSJDB/FX@:PSL
5
(com especificação de fuso horário)
Se for um evento de um dia, mas sem hora definida (exemplo:
data de aniversário), deve ser especificado o DTSTART no padrão AAAAMMDD e sem DTEND.
t %5&/%o&TQFDJmDBBEBUBIPSBEFmNEPFWFOUP
t %63"5*0/ o &TQFDJmDB B EVSBÎÍP EP FWFOUP DPOGPSNF B
ISO8601, exemplo:
P15DT5H0M20S – 15 dias, 5 horas e 20 segundos
P1DT12H45M – 1 dia, 12 horas e 45 minutos
P2W – 2 semanas
t 53"/41 o *OEJDB TF P FWFOUP JSÈ PDVQBS FTQBÎP OB BHFOEB
ou apenas ficará registrado, deixando o seu período livre para
mais compromissos. Usará o valor OPAQUE para ocupar a
agenda, senão deve ser TRANSPARENT para ficar registrado,
mas deixando o horário livre para novos agendamentos. O
padrão é OPAQUE.
t 03("/*;&3o*OEJDBPPSHBOJ[BEPSEPFWFOUP4FDPMPDBSPT
seus dados como ORGANIZER e enviar o e-mail do agendamento para si mesmo, este não será absorvido pelo aplicativo
de calendário, pois parte-se do princípio que como você é o
organizador o agendamento partiu daquele aplicativo. ExemQMP 03("/*;&3$/%ÏDJP -VDLPXNBJMUPEFDJPMVDLPX!
gmail.com.
t 6*%o²PJEFOUJmDBEPSÞOJDPEPBHFOEBNFOUPÏBUSBWÏTEFMF
que o aplicativo de calendário irá saber quando recebeu uma
atualização para um mesmo UID. Deve-se pensar um valor
para este campo que não se repita no mundo. O aconselhável
é que seja alguma composição com domínio, por exemplo:
agenda003_javaparaweb.com.br. Utilizando um domínio próprio é mais difícil dele se repetir no destino.
t $3&"5&% o &TQFDJmDB RVBOEP P BHFOEBNFOUP GPJ DSJBEP
Não tem relação com a data de início ou de fim.
t -"45.0%*'*&%o&TQFDJmDBRVBOEPPBHFOEBNFOUPGPJNPdificado pela última vez, usa o mesmo formato de data do
DTSTART.
33
: : www.mundoj.com.br : :
t 4&26&/$& o &TQFDJmDB B TFRVÐODJB EF BMUFSBÎÍP EP BHFOdamento. No momento da criação – se for informado – deve
ser 0. Se for enviada (por e-mail) alguma atualização deste
agendamento deverá ser incrementado, para cada alteração
enviada.
Listagem 2. Exemplo de agendamento completo.
BEGIN:VCALENDAR
PRODID:-//Software SA//Agenda Corporativa//PT
VERSION:2.0
Opções do VALARM
METHOD:REQUEST
BEGIN:VEVENT
O VALARM especifica como você será avisado do seu compromisso. Ele deve obrigatoriamente conter as propriedades ACTION e
TRIGGER, além de outras opcionais.
t "$5*0/o$POmHVSBRVBMBÎÍPEFWFTFSUPNBEBQBSBBWJTBS
do compromisso. Pode ter os valores AUDIO, DISPLAY ou EMAIL.
o AUDIO – Quando utilizado pode incluir uma propriedade
ATTACH que deve apontar para um arquivo de áudio que
será reproduzido quando o alarme for disparado.
o DISPLAY – Quando utilizado pode incluir a propriedade
DESCRIPTION. Esta propriedade conterá o texto que será
exibido quando o alarme for disparado.
o E-MAIL – Quando utilizado deve obrigatoriamente incluir
as propriedades DESCRIPTION como o corpo do e-mail
e SUMMARY como o assunto do e-mail, dentro do VALARM. Um ou mais ATTENDEE como destinatários e opcionalmente um ATTACH para o e-mail que será enviado.
t 3&1&"5 o $POmHVSB RVBOUBT WF[FT P BMBSNF EFWF TFS SFQFtido, depois do primeiro disparo. Se o valor for maior que
um deve configurar também a propriedade DURATION, no
mesmo padrão da DURATION do VEVENT.
t 53*((&3o$POmHVSBRVBOEPPBMBSNFTFSÈEJTQBSBEP4FSÈ
um valor negativo para disparar antes e positivo para depois
da DTSTART. Tem o mesmo formato da propriedade DURATION. Exemplos:
TRIGGER:-PT15M – Quinze minutos antes.
53*((&33&-"5&%&/%15.o$JODPNJOVUPTEFQPJTEP
final do evento.
t "55&/%&& o %FmOF PT EFTUJOBUÈSJPT DBTP P BMBSNF TFKB P
envio de e-mail. Exemplo: ATTENDEE:mailto:[email protected]. Para colocar mais destinatários deve-se repetir a
propriedade em mais linhas.
t "55"$)o%FmOFVNBSRVJWPBOFYPBPBHFOEBNFOUP/FTUF
caso será o arquivo de áudio associado ao alarme. Exemplo:
FMTTYPE=audio/basic:ftp://servidor.com.br/pub/sounds/
alarme.aud
Explorando um exemplo completo
A Listagem 2 apresenta um exemplo mais completo de agendamento, que contempla também um VALARM. Por mais que todo
desenvolvedor tenha vontade de endentar o código-fonte deste
VCALENDAR isto tornará o arquivo inválido. Além disso, principalmente se tratando de uma geração a partir de sistema, é necessário que cada propriedade tenha uma quebra de linha no final.
Devemos dar atenção especial à propriedade DESCRIPTION.
Para incluir quebra de linha deve ser adicionado o caractere \n.
Para que esta propriedade ocupe mais linhas dentro do objeto
iCalendar é necessário deixar dois espaços em branco na frente
das linhas posteriores.
34
UID:reuniao023-minhaempresa.com.br
ORGANIZER;CN="Administrador":mailto:[email protected]
DTSTART:20110414T140000
DTEND: 20110414T160000
LOCATION:Sala de Reunião 3
SEQUENCE:0
STATUS:CONFIRMED
CATEGORIES:APPOINTMENT
SUMMARY:Reunião de resultados do trimestre
DESCRIPTION:Avaliação dos indicadores \ne tomada de ação
para o próximo
trimestre
PRIORITY:5
CLASS:PUBLIC
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
Enviando por e-mail
O envio por e-mail é com certeza a aplicação mais interessante
para a geração de agendamentos. Porém este recurso tem algumas
particularidades que garantirão que o aplicativo que receberá o
e-mail reconheça esta mensagem como um agendamento.
O e-mail enviado terá um conteúdo do tipo text/calendar e no
formato UTF-8, que é o padrão para o formato iCalendar. Além
disso, o e-mail enviado não precisa ter assunto e corpo, o aplicativo de calendário irá considerar o SUMMARY e DESCRIPTION do
agendamento recebido.
Para o funcionamento do envio de e-mail, o projeto Java na sua
IDE preferida precisa ter os jars do Java Mail API e Java Activation
Framework, que podem ser obtidos nos endereços http://www.
oracle.com/technetwork/java/index-138643.html e http://www.
oracle.com/technetwork/java/javase/jaf-136260.html.
Estas duas bibliotecas necessárias têm como objetivo atender
exclusivamente ao envio de e-mail no Java, não existe qualquer
requisito específico para a geração do agendamento.
Veja na Listagem 3 como implementar o envio de e-mail de agendamento no Java.
Listagem 3. Exemplo de envio de agendamento por e-mail.
package br.com.mundoj.calendar;
import java.util.Properties;
import javax.activation.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.util.*;
public class CalendarMail {
public static void main(String[] args) throws Exception {
Properties config = new Properties();
config.put(“mail.smtp.host”, “smtp.servidor.com.br”);
config.put(“mail.smtp.port”, “25”);
Neste exemplo, a montagem do arquivo do iCalendar é feita dentro de um StringBuffer, perceba que sempre ao final de cada linha
adicionamos um \n, para incluir a quebra de linha obrigatória.
Ao criar o ByteArrayDataSource, obtemos o conteúdo do StringBuffer usando o método getBytes(“UTF-8”) o que garante que
não ocorram problemas de acentuação no agendamento. Além
EJTTPPTFHVOEPQBSÉNFUSPDPOUÏNPUFYUPUFYUDBMFOEBSNFUIPE
3&26&45DIBSTFU65'&TUFUFYUPJOEJDBPNJNFUZQFUFYU
calendar, o method que deve ser sempre igual ao METHOD do
agendamento e o charset. O código restante adiciona o conteúdo
como agendamento como um conteúdo do e-mail.
Para enviar este e-mail usando um servidor de e-mail autenticado,
consulte o quadro “Enviando e-mail com autenticação”.
Na figura 1 pode ser visto como este agendamento foi recebido no
Gmail, que se integra automaticamente ao Google Calendar.
Session session = Session.getDefaultInstance(config);
MimeMessage mimeMessage = new MimeMessage(session);
mimeMessage.setFrom(new InternetAddress(
“[email protected]”));
mimeMessage.addRecipients(Message.RecipientType.TO,
“[email protected]”);
mimeMessage.addRecipients(Message.RecipientType.TO,
“[email protected]”);
StringBuffer sb = new StringBuffer();
sb.append(“BEGIN:VCALENDAR\n”);
sb.append(“PRODID:-//Revista MundoJ//Teste do artigo//PT\n”);
sb.append(“VERSION:2.0\n”);
sb.append(“METHOD:REQUEST\n”);
sb.append(“BEGIN:VEVENT\n”);
sb.append(“UID:reuniao023-minhaempresa.com.br\n”);
sb.append(“ORGANIZER;CN=\”Diretor Comercial\”:mailto:
[email protected]\n”);
sb.append(“DTSTART:20110415T203000\n”);
sb.append(“DTEND:20110415T210000\n”);
sb.append(“LOCATION:Sala de reunião 3 - Térreo\n”);
sb.append(“STATUS:CONFIRMED\n”);
sb.append(“CATEGORIES:APPOINTMENT\n”);
sb.append(“SUMMARY:Reunião gerencial\n”);
sb.append(“DESCRIPTION:Elaboração de nova \\nestratégia comercial\n”);
sb.append(“ para o próximo trimestre\n”);
sb.append(“END:VEVENT\n”);
sb.append(“END:VCALENDAR”);
ByteArrayDataSource ds = new ByteArrayDataSource(sb.toString().
getBytes(“UTF-8”),
“text/calendar;method=REQUEST;charset=\”UTF-8\””);
DataHandler dh = new DataHandler(ds);
Multipart multipart = new MimeMultipart();
MimeBodyPart mimepart = new MimeBodyPart();
mimepart.setDataHandler(dh);
multipart.addBodyPart(mimepart);
mimeMessage.setContent(multipart);
Transport.send(mimeMessage);
}
}
Figura 1. Agendamento recebido no GMail e Google Calendar.
Criando um arquivo
Um agendamento também poderia ser transportado por meio de
um arquivo simples. Para isso, bastaria gerar um arquivo com
um conteúdo no mesmo padrão da Listagem 2 e nomeá-lo como
.ics (Internet Calendar Service). Ao acionar este arquivo em um
computador com algum aplicativo de calendário, este será aberto,
bastando aceitar o agendamento para que ele entre na sua agenda.
Criando um serviço de calendário
Um serviço de calendário é um recurso existente em aplicativos
como o Google Calendar ou o Mozila Sunbird que permite manter a agenda sempre sincronizada com informações de calendário
disponíveis na internet.
Estas informações de calendário devem ser disponibilizadas por
meio de uma URL que aponte para um arquivo no formato iCalendar. Este não precisa ser necessariamente um arquivo físico,
as informações podem também ser geradas sob demanda, o importante é que sejam no formato iCalendar. Um exemplo de URL
poderia ser http://www.seudominio.com.br/eventosjava_ical.jsp,
ao chamar este endereço, um conteúdo no formato iCalendar seria devolvido. Para que um arquivo possa conter informações de
mais de um agendamento ao mesmo tempo deve seguir o padrão
conforme a Listagem 4.
35
: : www.mundoj.com.br : :
Listagem 4. Formato para serviço de calendário
BEGIN:VCALENDAR
PRODID:-//Software SA//Agenda Corporativa//PT
VERSION:2.0
METHOD:REQUEST
Enviando e-mail com autenticação
Caso o seu servidor de e-mail utilize autenticação, serão
necessárias algumas linhas de código a mais. Estas linhas
adicionam a propriedade mail.smtp.auth como true e inclui
as informações de autenticação, conforme a Listagem 5.
BEGIN:VEVENT
[...]
END:VEVENT
BEGIN:VEVENT
[...]
END:VEVENT
BEGIN:VEVENT
Listagem 5. Autenticação para envio de e-mail.
Properties config = new Properties();
config.put(“mail.smtp.host”, “smtp.servidor.com.br”);
config.put(“mail.smtp.port”, “25”);
config.put(“mail.smtp.auth”, “true”);
[...]
END:VEVENT
END:VCALENDAR
Perceba que este arquivo contém somente um elemento VCALENDAR e dentro dele vários VEVENT que deverão conter as informações dos agendamentos em si. Para adicionar um calendário da
internet no Google Calendar vá em Outras Agendas > Adicionar
> Adicionar por URL e informe a URL do calendário. No Mozilla
Sunbird vá em Arquivo > Inscrever em calendário remoto. Selecione a opção “Na Rede” quanto a localização do calendário e
depois a opção “iCalendar (ICS)” para o formato, e informe a URL.
Atualizando um agendamento
Para o envio de agendamento por e-mail, caso este tenha que ser
atualizado é necessário seguir algumas regras. Para o serviço de
calendário na internet isto não é necessário, pois a sincronização
é constante.
Sempre que for necessário enviar uma nova versão de um agendamento por e-mail, deve ser dada especial atenção ao UID. Esta
propriedade identificará unicamente o agendamento, permitindo
atualizar um agendamento já existe e não criar um novo.
Além disso, a propriedade SEQUENCE deve ser incrementada
conforme as atualizações forem enviadas. Por padrão, esta propriedade tem o valor 0, se for enviada uma atualização o seu valor
tem que ser 1, depois 2 conforme forem enviadas mais atualizações. Todas as propriedades do agendamento devem ser enviadas
e não somente aquelas que sofreram alteração.
Cancelando um agendamento
No "cancelando", a propriedade UID tem a mesma função da
atualização de agendamento. Porém no caso de um cancelamento
a propriedade METHOD deve ser enviada como CANCEL tanto
no corpo do VCALENDAR quanto na linha de envio de e-mail,
FYFNQMPUFYUDBMFOEBSNFUIPE$"/$&-DIBSTFU65'
Além disso, a propriedade STATUS no corpo do VEVENT deve
ter o valor CANCELLED. Todas as propriedades do agendamento
devem ser enviadas, mesmo que seja apenas um cancelamento.
36
javax.mail.Authenticator autenticacao = new javax.mail.Authenticator() {
@Override
protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
return new javax.mail.PasswordAuthentication(“usuario”, “senha”);
}
};
Session session = Session.getDefaultInstance(config, autenticacao);
Livro Programação Java para a Web
Este artigo apresentou o padrão iCalendar, que é um recurso
muito interessante para agregar funcionalidade ao seu aplicativo. Com ele, é possível integrar o seu produto aos aplicativos de calendário já utilizados por seus usuários, como o
Google Calendar, Mozilla Sunbird, Microsoft Outlook, Lotus
Notes, entre outros. Como exemplo de uso pode-se citar:
avisos de pagamento, agendamento de consultas médicas e
qualquer outro tipo de compromisso.
Esta abordagem de utilizar exemplos interessantes para ensinar Java também é adotada pelo livro Programação Java
para a Web da editora Novatec (www.javaparaweb.com.
br). Este livro possui uma temática inovadora, com enfoque
extremamente prático, que mostra passo a passo como desenvolver uma aplicação web utilizando a linguagem Java
e as tecnologias mais poderosas e populares no arsenal dos
desenvolvedores, como JavaServer Faces e Hibernate.
O método utilizado no livro se baseia no projeto de uma
aplicação financeira pessoal, em que são abordadas várias
técnicas de desenvolvimento em cada etapa do projeto, desde as mais tradicionais e conhecidas até as mais modernas e
avançadas.
O artigo apresenta recursos que podem complementar seus
próprios projetos e o projeto abordado no livro, tornando-os
ainda mais interessantes. Além disso, o livro também aborda
os requisitos básicos de um sistema construído de forma
profissional em Java, como: JavaServer Faces, Hibernate,
Facelets, CSS, Spring Security, Ajax, PrimeFaces, internacionalização, iReport e Jasper Reports, JfreeChart, WebServices
e busca de informações em meios externos.
Considerações finais
Este artigo mostrou um recurso muito interessante tanto para
aplicativos web quanto desktop. Seu objetivo não foi esgotar este
assunto, mas mostrar as configurações mais comuns para seu uso.
Além disso, mostrou algumas opções de disponibilização dos caMFOEÈSJPTFOWJBOEPQPSFNBJMQPSBSRVJWPFWJBJOUFSOFUt
Referências
-JWSP1SPHSBNBÎÍP+BWBQBSBB8FC-VDLPX%ÏDJP)FJO[FMNBOO.FMP"MFYBOESF"MUBJSEF&EJUPSB/PWBUFD
-JTUBEFBQMJDBUJWPTRVFBDFJUBNJ$BMFOEBSIUUQFOXJLJQFEJBPSHXJLJ-JTU@PG@
BQQMJDBUJPOT@XJUI@J$BMFOEBS@TVQQPSU
J$BMFOEBSOB8JLJQÏEJBIUUQFOXJLJQFEJBPSHXJLJ*$BMFOEBS
&TQFDJmDBÎÍPPmDJBMEPJ$BMFOEBSIUUQUPPMTJFUGPSHIUNMSGD
GUJ – Discussões sobre o tema do artigo e
assuntos relacionados
Discuta este artigo com 100 mil outros
desenvolvedores em www.guj.com.br/MundoJ
37
Download