SlideShare ist ein Scribd-Unternehmen logo
1 von 65
Downloaden Sie, um offline zu lesen
Migrando aplicações do
mundo real para o Java
SE 8
Janario Oliveira | @janarioliver
Michael Nascimento Santos | @mr_ _m
Michel Graciano | @mgraciano
Apresentação
● Michael Nascimento Santos
○ 14 anos de experiência com a plataforma Java e programador há 20
anos
○ Committer do OpenJDK
○ Membro da organização do SouJava
○ JavaOne Rock Star Speaker
○ Co-líder da JSR-310 (Date & Time API - java.time) e expert em mais 6
JSRs, inclusive a que definiu o Java SE 6
○ Líder, arquiteto e desenvolvedor na TecSinapse
● Janario Oliveira
○ Mais de 4 anos de experiência com a plataforma Java
○ Contribuições ativas em projetos opensource como Hibernate, JBoss
AS, NetBeans entre outros
○ Desenvolvedor na TecSinapse
Apresentação
● Michel Graciano
○ Atualmente Arquiteto de Sistemas na Betha Sistemas e com mais de
10 anos de experiência com a plataforma Java
○ Membro ativo de projetos open source como o NetBeans e genesis
○ Já fez apresentações no JavaOne USA e Brasil, bem como em
algumas edições do TDC Floripa e JustJava.
Apresentação
Agenda
● Introdução rápida
● Migrando aplicações para Java SE 8
○ O que podemos migrar automaticamente
○ Tentando aprofundar o uso dos novos recursos
● Dificuldades e perdas de performance
● Conclusão
● Q&A
Disclaimer
Nosso código e testes foram realizados com
o b97(30/06/2013) do Lambda
#java8mundoreal
Introdução rápida
Introdução aos principais conceitos e
tecnologias do Java SE 8
JSR 337: Java SE 8
● Datas
○ 2013/09/05 Developer Preview
○ 2014/01/23 Release Candidate
○ 2014/03/18 Final Release
● Principais JSRs
○ 294: Improved Modularity Support in the JavaTM Programming
Language (Jigsaw)
○ 308: Annotations on Java Types (não tem API prática ainda)
○ 310: Date and Time API
○ 335: Lambda Expressions for the JavaTM Programming Language
JSR 310: Date and Time
● Spec Leads:
○ Stephen Colebourne - criador do Joda-Time
○ Michael Nascimento Santos
○ Roger Riggs
● Baseado e muito semelhante ao Joda-Time, porém melhor
JSR 310: Date and Time
● Imutável e thread-safe
● Utilize sempre as classes mais específicas para o problema
● YearMonth - Mês e ano
YearMonth.of(2013, Month.JULY);
● LocalDate - Data sem hora ou time-zone
LocalDate.now();
LocalDate dataTDC = LocalDate.of(2013, Month.JULY, 12);
● LocalTime - Hora sem data ou time-zone
LocalTime meiaNoite = LocalTime.MIDNIGHT;
LocalTime onzeHoras = LocalTime.of(11, 0);
assert meiaNoite.isBefore(onzeHoras);
JSR 310: Date and Time
● LocalDateTime - Data com hora sem time-zone
LocalDateTime dataTDCMeioDia = LocalDateTime. of(dataTDC,
LocalTime.NOON);
LocalDateTime dataTDCOnzeHoras = dataTDC.atTime(11, 0);
assert dataTDCMeioDia. minusHours(1).equals(dataTDCOnzeHoras );
● OffsetDateTime - Data com hora offset e sem time-zone
OffsetDateTime. of(dataTDCMeioDia, ZoneOffset. ofHours(-3));
● ZonedDateTime - Data com hora e time-zone
ZonedDateTime. of(dataTDCMeioDia, ZoneId.of( "America/Sao_Paulo" ));
JSR 310: Date and Time
● Outras classes de domínio
○ Year
○ Month - enum
○ DayOfWeek - enum
○ OffsetDate
○ OffsetTime
○ Period
○ Instant
○ Duration
○ Clock
● Nova API de formatação
● Diversos outros conceitos:
○ Temporals
○ Adjusters
○ Queries
○ Units
JSR 335:
Lambda Expressions
● Permite programação funcional, com maior nível de reutilização de código
e escrita concisa
int maiorIdadeDePessoaDoSexoMasculino = -1;
for (Pessoa pessoa : pessoas) {
if (pessoa.getSexo() == Sexo.MASCULINO) {
int idade = pessoa.getIdade();
if (idade > maiorIdadeDePessoaDoSexoMasculino ) {
maiorIdadeDePessoaDoSexoMasculino = idade;
}
}
}
if (maiorIdadeDePessoaDoSexoMasculino != -1) {
trataIdade (maiorIdadeDePessoaDoSexoMasculino );
}
JSR 335:
Lambda Expressions
● Permite programação funcional, com maior nível de reutilização de código
e escrita concisa
pessoas.stream()
.filter(pessoa -> pessoa.getSexo() == Sexo.MASCULINO)
.mapToInt(Pessoa::getIdade)
.max()
.ifPresent(PessoaProcessor ::trataIdade);
JSR 335:
Lambda Expressions
● Permite programação funcional, com maior nível de reutilização de código
e escrita concisa
pessoas.parallelStream()
.filter(pessoa -> pessoa.getSexo() == Sexo.MASCULINO)
.mapToInt(Pessoa::getIdade)
.max()
.ifPresent(PessoaProcessor ::trataIdade);
Migrando aplicações
para Java SE 8
Migrando aplicações para
Java SE 8
● Foram migradas duas aplicações:
○ Um BI customizado para indústria automobilística com diversos
gráficos e relatórios
○ Uma aplicação 24x7 que será lançada em breve
● Ambas com grande utilização do Guava, o que facilitou muito a migração
para utilização de Lambda Expressions
○ Guava(code.google.com/p/guava-libraries) - Framework utilitário com
suporte a programação funcional
● Forte utilização do Joda-Time, em especial o YearMonth por serem
gráficos que acumulam dados estatísticos mensais
● Iniciamos há 8 meses e muita coisa vem sendo melhorada neste período
O que podemos migrar
automaticamente
● NetBeans 8 Nightly Builds está em desenvolvimento e já oferece algumas
Hints para o Java SE 8 (Refactor > Inspect and Transform):
○ Hint: Convert to Lambda
O que podemos migrar
automaticamente
● NetBeans 8 Nightly Builds está em desenvolvimento e já oferece algumas
Hints para o Java SE 8 (Refactor > Inspect and Transform):
○ Hint: Use Functions Operations
O que podemos migrar
automaticamente
● Benéfica principalmente para projetos Java SE
○ Usam mais Functional Interfaces (interfaces de um método abstrato
apenas), boas candidatas à migração
○ Runnable, listeners do Swing etc. são exemplos
● Projetos Java EE só se beneficiarão mais se usarem alguma biblioteca
funcional
○ Nós usamos :-)
Tentando aprofundar o uso
dos novos recursos
● Guava nos ajudou na migração automática, mas agora precisávamos
eliminar para testar a API
● As operações funcionais mais comuns do Guava tem equivalente quase
direto no Java SE 8:
○ filter -> filter
○ transform -> map
○ limit -> limit
● E o resto?
Tentando aprofundar o uso
dos novos recursos
● Como converter o resultado de uma operação funcional para uma List?
List<String> names =
brands.stream()
.map(Brand::getName)
.collect(toList());
● Através de collectors (implementações padrão em Collectors) é que
fazemos a maior parte das "terminal operations", i.e., converter de um
stream para outra collection ou classe "sintetizadora" do resultado
Tentando aprofundar o uso
dos novos recursos
● Como gerar um Map<Long,Brand>?
//Padrão throwingMerger: java.lang.IllegalStateException: Duplicate key
Map<Long,Brand> brandById =
brands.stream()
.collect(toMap(Brand::getId, identity()));
● Mas e se houver colisões?
○ Um parâmetro adicional, quando especificado, define a estratégia de
"merge":
BinaryOperator<T>
T apply(T u, T v)
(u,v) -> u; //firstWinsMerger () método removido no b97
(u,v) -> v; //lastWinsMerger () método removido no b97
Tentando aprofundar o uso
dos novos recursos
● Mas e quando preciso de uma lista com colisões?
Map<Holding,List<Brand>> brandByHolding =
brands.stream()
.collect(groupingBy(Brand::getHolding));
Tentando aprofundar o uso
dos novos recursos
● Como agregar elementos de uma coleção retornada pelo objeto do
stream?
List<Dealer> branches =
dealers. stream()
.flatMap(dealer -> dealer.getBranches().stream())
.collect(toList());
Tentando aprofundar o uso
dos novos recursos
● Novos métodos úteis em Map:
//Java 7
Long l = totalByYearMonth.get(yearMonth);
long total = l == null ? 0L : l;
//Java 8
long total = totalByYearMonth. getOrDefault(yearMonth, 0L);
Tentando aprofundar o uso
dos novos recursos
● Novos métodos úteis em Map:
//Java 7
Map<Brand,Long> totalByBrand = totalByBrandByYearMonth.get(yearMonth);
if (totalByBrand == null) {
totalByBrandByYearMonth.put(yearMonth, totalByBrand = new
HashMap<>());
}
Long t = totalByBrand.get(brand);
totalByBrand.put(brand, t == null ? total : t + total);
//Java 8
totalByBrandByYearMonth
.putIfAbsent(yearMonth, new HashMap<>())
.merge(brand, total, Long::sum);
Date and Time
● Formatador - por ser thread-safe é possível defini-lo em uma variável
estática e utilizar em diversos ponto
public static final DateTimeFormatter ANO_MES_FORMATTER =
DateTimeFormatter. ofPattern("MMM/yyyy", new Locale("pt", "BR"));
Date and Time
● A API prover diversos métodos e formas para que seja efetuado cálculos
com data
YearMonth start = YearMonth.now();
YearMonth end = YearMonth.now().plusMonths(1);
int days = ChronoUnit.DAYS.between(start.atDay(1), end.atEndOfMonth())
.getDays();
Date and Time
● Métodos para comparações
YearMonth yearMonth = YearMonth.of(year, month);
if (yearMonth.isAfter(YearMonth.now())) {
//processa data futura...
}
Date and Time (Hibernate)
● Alguns lugares temos a persistência de LocalDate e LocalDateTime, como
persistir com JPA(Hibernate)? UserType
interface UserType {
boolean isMutable();//não
/** It is not necessary to copy immutable objects */
Object deepCopy (Object value);
/** should perform a deep copy if the type is mutable */
Serializable disassemble (Object value);
/** should perform a deep copy if the type is mutable */
Object assemble (Serializable cached, Object owner );
/** For immutable objects it is safe to simply return the
first parameter */
Object replace (Object original, Object target, Object owner );
}
Date and Time (Hibernate)
● Criar duas classes muito parecidas ou uma classe abstrata que
implementa o comportamento parecido das duas? Nenhuma; default
methods
public interface ImmutableUserType extends UserType {
@Override public default boolean equals(Object x, Object y) {
return Objects.equals(x, y);
}
@Override public default int hashCode(Object x) {
return Objects.hashCode(x);
}
@Override public default boolean isMutable() {
return false;
}
...
Date and Time (Hibernate)
● E mais métodos:
...
@Override public default Object deepCopy(Object value) {
return value;
}
@Override public default Serializable disassemble(Object value) {
return (Serializable) value;
}
@Override public default Object assemble(Serializable cached,
Object owner) {
return cached;
}
@Override public default Object replace(Object original,
Object target, Object owner) {
return original;
}
}
● LocalDateTimeType
public class LocalDateTimeType implements ImmutableUserType {
@Override public int[] sqlTypes() {
return new int[] { Types.TIMESTAMP}; }
@Override public Class<LocalDateTime> returnedClass () {
return LocalDateTime. class;}
@Override public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner) {
Timestamp persistValue = (Timestamp) rs.getObject(names
[0]);
if (rs.wasNull()) { return null; }
return persistValue. toLocalDateTime ();
}
...
Date and Time (Hibernate)
● LocalDateTimeType
...
@Override
public void nullSafeSet(PreparedStatement st, Object value,
int index, SessionImplementor session ) {
if (value == null) { st.setNull(index, sqlTypes ()[0]);
} else {
Timestamp timestamp = Timestamp
.valueOf((LocalDateTime) value);
st.setObject(index, timestamp, sqlTypes ()[0]);
}
}
}
Date and Time (Hibernate)
Dificuldades
Dificuldades
Dificuldades
Dificuldades
● Nossos estressados membros do EG, especialmente nosso amigo Brian,
às vezes dão respostas "delicadas"
○ Porém ele pede desculpas em pvt depois, acreditem ou não :-)
● Nem sempre é muito fácil achar os métodos na API e precisa-se do apoio
da lista
○ Pelo menos eles respondem muito rápido!
● Alguns métodos que mostramos que existem na API hoje foram resultados
dessas discussões
○ Inclusive o getOrDefault, pro qual o Brian também deu uma resposta
delicada de primeira, mas tá aí agora
● A API mudou de forma incompatível diversas vezes durante esse período,
fazendo com que às vezes perdêssemos 1 dia inteiro só para deixar tudo
recompilando com lambda de novo :-(
○ When you're living on the bleeding edge, you should not be surprised
when you do, in fact, bleed
Formatação e estilo
● A formatação e estilo do código afeta bastante a legibilidade (mais do que
nunca):
List<String> emailsOrdenados = pessoas.stream().filter((Pessoa pessoa) ->
pessoa.getDataNascimento ().isBefore(dezAnosAtras)).map((Pessoa pessoa)
->
pessoa.getEmail()).sorted((String o1, String o2) -> o1
.compareToIgnoreCase (o2)).collect(Collectors.toList()) ;
● Versus:
List<String> emailsOrdenados =
pessoas.stream()
.filter(pessoa -> pessoa.getDataNascimento ().isBefore
(dezAnosAtras))
.map(Pessoa::getEmail)
.sorted(String::compareToIgnoreCase )
.collect(toList());
Suporte a Stream de Maps
Suporte a Stream de Maps
NÃO TEM!
Suporte a Stream de Maps
● Foi discutido pelo EG, mas descartado por exigir classes específicas e ser
melhor suportado com tuplas
● Tínhamos na nossa base vários casos funcionais de Map com Guava e
tivemos que converter para entrySet().stream()
● É tão feio que não daria tempo de vocês entenderem na palestra (é sério!)
● Vamos pensar seriamente se vale a pena manter na nossa base de código
com Java SE 8
Stream para array
● Como converter?
● Stream.toArray(IntFunction<A[]> generator)
Pessoa[] p = pessoas.stream()
//.filter .map ...
.toArray((value) -> {//IntFunction > R apply(int value)
//O que retornar?
//new Pessoa[0] como em List.toArray??
//new Pessoa[10] acho que vai ter 10 ???
//new Pessoa[]{};
//java.lang.IndexOutOfBoundsException: does not fit
});
Stream para array
● Como converter?
● Stream.toArray(IntFunction<A[]> generator)
Pessoa[] p = pessoas.stream()
//.filter .map ...
.toArray((value) -> {
return new Pessoa[value];
});
Stream para array
● Como converter?
● Stream.toArray(IntFunction<A[]> generator)
Pessoa[] p = pessoas.stream()
//.filter .map ...
.toArray(Pessoa[]::new); //modo idiomático
Acessos a recursos Java EE
● Algumas APIs Java EE, direta ou indiretamente, acreditam que podem
controlar a instância "mágica" disponível via ThreadLocal (ex:
FacesContext.getCurrentInstance())
● Com Lambda, elas falham miseravelmente com parallelStream()
● Soluções?
○ Não usar parallelStream() :-(
○ Criar na thread principal e sair passando
○ Fazer patch do seu container preferido (se o seu container vem de
uma empresa de 3 letrinhas, ele é todo baseado em threads pra
isso... boa sorte!)
○ Perturbar o Brian na lista para que haja uma SPI de criação do
mecanismo de execução de parallelStream() (Michael já cansou de
fazer isso... boa sorte!)
○ Parar de brincar com tecnologias não suportadas oficialmente :-)
● Nós incluímos uma abstração no meio (porque o Janario tá com preguiça :
-p)
Problemas - Spring
● Spring(ASM) [SPR-10292]
○ O ASM não conseguia interpretar o bytecode gerado
java.lang.IllegalArgumentException
at org.springframework.asm.ClassReader.<init>(Unknown Source)
● Reportado pelo Michael em 13/02/2013
● Solucionado 23/04/2013
● Será lançado na versão 4.0 utilizamos em nossos testes a versão
snapshot.
● Nestes meses continuamos nossa migração validando pelos testes de
integração
Problemas - Spring
● Spring - JDK (DocumentBuilderFactory(b92))
○ No build 92 do JDK exista uma restrição de segurança que não
permitia a requisição, durante a validação, de urls de namespace de
xmls
org.xml.sax.SAXException: schema_reference: Failed to read schema
document 'spring-beans-3.1.xsd', because 'http' access is not allowed.
● Soluções:
○ System property -Djavax.xml.accessExternalSchema=all
○ Chamada via api DocumentBuilderFactory.setAttribute("http://javax.
xml.XMLConstants/property/accessExternalSchema", "all");
● Não ocorre mais na última versão testada b97
Problemas - Spring
● Necessário utilizar o snapshot (enquanto não sair a versão final)
Problemas - JBoss(Jandex)
● Jandex (Java Annotation Indexer) JANDEX-14 - Um indexador de
anotações
○ Não conseguia interpretar classes com bytecode que continham
expressões lambda (invokedynamic constant pool tag 18)
java.lang.IllegalStateException: Unknown tag! pos=1 poolCount = 61
at org.jboss.jandex.Indexer.processConstantPool(Indexer.java:603)
● Reportado pelo Janario em 16/05/2013
● Pull request aceito 22/05/2013 (https://github.com/wildfly/jandex/pull/12) - Janario Oliveira
Problemas - JBoss x JDK
● ConcurrentSkipListSet - Ao adicionar os processors em um o mesmo fica
com chamadas infinitas ao compareTo do objeto adicionado.
org.jboss.as.server.deployment.RegisteredDeploymentUnitProcessor.compareTo(RegisteredDeploymentUnitProcessor.java:41)
org.jboss.as.server.deployment.RegisteredDeploymentUnitProcessor.compareTo(RegisteredDeploymentUnitProcessor.java:28)
java.util.concurrent.ConcurrentSkipListMap.findPredecessor(ConcurrentSkipListMap.java:696)
java.util.concurrent.ConcurrentSkipListMap.doPut(ConcurrentSkipListMap.java:843)
java.util.concurrent.ConcurrentSkipListMap.putIfAbsent(ConcurrentSkipListMap.java:2325)
java.util.concurrent.ConcurrentSkipListSet.add(ConcurrentSkipListSet.java:241)
org.jboss.as.server.DeployerChainAddHandler.addDeploymentProcessor(DeployerChainAddHandler.java:60)
● Não sabemos se é um bug no JDK ou no JBoss
● Utilizamos a versão customizada neste ponto em específico para evitar
este erro.
Problemas - Lombok
● Lombok
○ Issue #145 ainda em aberto desde 15/02/2013 :-(
○ Processor do Lombok não é compatível com o JavaC do Java SE 8
○ Incompatível com NetBeans 7.4, já que o JavaC do Java SE 8 é
utilizado pelo IDE para os parsings internos (Editor por exemplo)
● Reportado 15/02/2013 - Jan Lahoda
● Continua em aberto
● Apesar de não utilizarmos em nossos projetos, nosso amigo Michel
Graciano utiliza.
Performance
Compilação
Microbenchmark - Caliper
● For each - AtomicInteger em uma lista
//ForEachClassic
for (Integer integer : list) {
atomicInteger. accumulateAndGet (integer, Integer ::sum);
}
//ForEachStream
list.stream().forEach((integer) -> { ... });
//ForEachArrayList
list.forEach((integer) -> {...});
//ForEachParallelStream
list.parallelStream ().forEach((integer) -> {...});
Microbenchmark - Caliper
For each - AtomicInteger
Microbenchmark - Caliper
For each - Fatorial em todos valores de 0 a 2000
private final IntFunction<Integer> factorial = i -> {
return i == 0 ? 1 : i * factorial.apply(i - 1);
};
//ForEachClassic
for (Integer integer : list) {
factorial. apply(integer);
}
//ForEachStream
list.stream().forEach((integer) -> { ... });
//ForEachArrayList
list.forEach((integer) -> { ... });
//ForEachParallelStream
list.parallelStream ().forEach((integer) -> { ... });
Microbenchmark - Caliper
For each - Fatorial
Execução
Conclusão
● Migrar aplicações do mundo real para o Java SE 8 hoje é possível - se
você realmente souber Java e se elas tiverem testes
● As novas funcionalidades podem realmente tornar seu código bem mais
legível
● Ganhos de performance podem ser obtidos - mas sempre meça seu
código com ferramentas como Caliper, JMeter e um bom profiler
● Vários métodos e novos idiomas aceleram o desenvolvimento
● O Spring 4.0.0-SNAPSHOT *por enquanto* funciona, ao passo que o
JBoss, só com hacks
● Para facilitar a sua migração use Java 7 (pra começo de conversa), adote
o Guava e o backport da JSR 310 para Java 7 (https://github.
com/ThreeTen/threetenbp)
● Siga as listas e participe ativamente das mesmas
● Se você acha que seria capaz de fazer as coisas descritas nessa palestra
- e gostaria de ter tempo pago pela empresa para isso -, mande seu cv
para recrutamento@tecsinapse.com.br :-)
Obrigado!
Janario Oliveira | @janarioliver
Michael Nascimento Santos | @mr_ _m
Michel Graciano | @mgraciano
Agradecimentos
● Michel Graciano (@mgraciano) - Sem dúvida
uma grande ajuda na coleta e
organização do conteúdo destes slides
Q&A
Janario Oliveira | @janarioliver
Michael Nascimento Santos | @mr_ _m
Michel Graciano | @mgraciano

Weitere ähnliche Inhalte

Ähnlich wie Migrando aplicações do mundo real para o java se 8

Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7Christiano Anderson
 
Programação Orientada a Aspectos
Programação Orientada a AspectosProgramação Orientada a Aspectos
Programação Orientada a AspectosRicardo Terra
 
Plano de Projeto de Software do​ Residents Control
Plano de Projeto de Software do​ Residents ControlPlano de Projeto de Software do​ Residents Control
Plano de Projeto de Software do​ Residents Controlazarael2607
 
O que vi na QCon 2012 São Paulo
O que vi na QCon 2012 São PauloO que vi na QCon 2012 São Paulo
O que vi na QCon 2012 São PauloIsmael
 
RxJava - Programação assíncrona para Android.
RxJava - Programação assíncrona para Android.RxJava - Programação assíncrona para Android.
RxJava - Programação assíncrona para Android.Clerton Leal
 
It skills para rh aprender e contratar
It skills para rh  aprender e contratarIt skills para rh  aprender e contratar
It skills para rh aprender e contratarAle Uehara
 
JavaScript: uma visão geral da linguagem e seu ecossistema
JavaScript: uma visão geral da linguagem e seu ecossistemaJavaScript: uma visão geral da linguagem e seu ecossistema
JavaScript: uma visão geral da linguagem e seu ecossistemaFabio Agostinho Boris
 
Desmitificando as aplicações RESTFul usando Django Rest Framework
Desmitificando as aplicações RESTFul usando Django Rest FrameworkDesmitificando as aplicações RESTFul usando Django Rest Framework
Desmitificando as aplicações RESTFul usando Django Rest FrameworkBruno Oliveira
 
Visao geralti netshoes04
Visao geralti netshoes04Visao geralti netshoes04
Visao geralti netshoes04Ale Uehara
 
Visao geral TI04 2-0
Visao geral TI04 2-0Visao geral TI04 2-0
Visao geral TI04 2-0Ale Uehara
 
Apostilava Java EE 5 - 2007
Apostilava Java EE 5 - 2007Apostilava Java EE 5 - 2007
Apostilava Java EE 5 - 2007Rafael Benevides
 
Cross testing mobile com ruby, cucumber e appium
Cross testing mobile com ruby, cucumber e appiumCross testing mobile com ruby, cucumber e appium
Cross testing mobile com ruby, cucumber e appiumMaximiliano Alves
 
A Evolução do Front end
A Evolução do Front endA Evolução do Front end
A Evolução do Front endDouglas Matoso
 

Ähnlich wie Migrando aplicações do mundo real para o java se 8 (20)

Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7Django e MongoDB - Python Brasil 7
Django e MongoDB - Python Brasil 7
 
Revista programar 21
Revista programar 21Revista programar 21
Revista programar 21
 
Programação Orientada a Aspectos
Programação Orientada a AspectosProgramação Orientada a Aspectos
Programação Orientada a Aspectos
 
Plano do Projeto
Plano do ProjetoPlano do Projeto
Plano do Projeto
 
Plano de Projeto de Software do​ Residents Control
Plano de Projeto de Software do​ Residents ControlPlano de Projeto de Software do​ Residents Control
Plano de Projeto de Software do​ Residents Control
 
SAFe - Como escalar algo artesanal?
SAFe - Como escalar algo artesanal?SAFe - Como escalar algo artesanal?
SAFe - Como escalar algo artesanal?
 
O que vi na QCon 2012 São Paulo
O que vi na QCon 2012 São PauloO que vi na QCon 2012 São Paulo
O que vi na QCon 2012 São Paulo
 
RxJava - Programação assíncrona para Android.
RxJava - Programação assíncrona para Android.RxJava - Programação assíncrona para Android.
RxJava - Programação assíncrona para Android.
 
It skills para rh aprender e contratar
It skills para rh  aprender e contratarIt skills para rh  aprender e contratar
It skills para rh aprender e contratar
 
ASP.NET MVC Core
ASP.NET MVC CoreASP.NET MVC Core
ASP.NET MVC Core
 
JavaScript: uma visão geral da linguagem e seu ecossistema
JavaScript: uma visão geral da linguagem e seu ecossistemaJavaScript: uma visão geral da linguagem e seu ecossistema
JavaScript: uma visão geral da linguagem e seu ecossistema
 
Desmitificando as aplicações RESTFul usando Django Rest Framework
Desmitificando as aplicações RESTFul usando Django Rest FrameworkDesmitificando as aplicações RESTFul usando Django Rest Framework
Desmitificando as aplicações RESTFul usando Django Rest Framework
 
Visao geralti netshoes04
Visao geralti netshoes04Visao geralti netshoes04
Visao geralti netshoes04
 
Visao geralti netshoes04
Visao geralti netshoes04Visao geralti netshoes04
Visao geralti netshoes04
 
Visao geral TI04 2-0
Visao geral TI04 2-0Visao geral TI04 2-0
Visao geral TI04 2-0
 
Python e MongoDB - Ensol
Python e MongoDB - EnsolPython e MongoDB - Ensol
Python e MongoDB - Ensol
 
Apostilava Java EE 5 - 2007
Apostilava Java EE 5 - 2007Apostilava Java EE 5 - 2007
Apostilava Java EE 5 - 2007
 
Cross testing mobile com ruby, cucumber e appium
Cross testing mobile com ruby, cucumber e appiumCross testing mobile com ruby, cucumber e appium
Cross testing mobile com ruby, cucumber e appium
 
Spring Data Neo4j
Spring Data Neo4jSpring Data Neo4j
Spring Data Neo4j
 
A Evolução do Front end
A Evolução do Front endA Evolução do Front end
A Evolução do Front end
 

Migrando aplicações do mundo real para o java se 8

  • 1. Migrando aplicações do mundo real para o Java SE 8 Janario Oliveira | @janarioliver Michael Nascimento Santos | @mr_ _m Michel Graciano | @mgraciano
  • 2. Apresentação ● Michael Nascimento Santos ○ 14 anos de experiência com a plataforma Java e programador há 20 anos ○ Committer do OpenJDK ○ Membro da organização do SouJava ○ JavaOne Rock Star Speaker ○ Co-líder da JSR-310 (Date & Time API - java.time) e expert em mais 6 JSRs, inclusive a que definiu o Java SE 6 ○ Líder, arquiteto e desenvolvedor na TecSinapse
  • 3. ● Janario Oliveira ○ Mais de 4 anos de experiência com a plataforma Java ○ Contribuições ativas em projetos opensource como Hibernate, JBoss AS, NetBeans entre outros ○ Desenvolvedor na TecSinapse Apresentação
  • 4. ● Michel Graciano ○ Atualmente Arquiteto de Sistemas na Betha Sistemas e com mais de 10 anos de experiência com a plataforma Java ○ Membro ativo de projetos open source como o NetBeans e genesis ○ Já fez apresentações no JavaOne USA e Brasil, bem como em algumas edições do TDC Floripa e JustJava. Apresentação
  • 5. Agenda ● Introdução rápida ● Migrando aplicações para Java SE 8 ○ O que podemos migrar automaticamente ○ Tentando aprofundar o uso dos novos recursos ● Dificuldades e perdas de performance ● Conclusão ● Q&A
  • 6. Disclaimer Nosso código e testes foram realizados com o b97(30/06/2013) do Lambda
  • 8. Introdução rápida Introdução aos principais conceitos e tecnologias do Java SE 8
  • 9. JSR 337: Java SE 8 ● Datas ○ 2013/09/05 Developer Preview ○ 2014/01/23 Release Candidate ○ 2014/03/18 Final Release ● Principais JSRs ○ 294: Improved Modularity Support in the JavaTM Programming Language (Jigsaw) ○ 308: Annotations on Java Types (não tem API prática ainda) ○ 310: Date and Time API ○ 335: Lambda Expressions for the JavaTM Programming Language
  • 10. JSR 310: Date and Time ● Spec Leads: ○ Stephen Colebourne - criador do Joda-Time ○ Michael Nascimento Santos ○ Roger Riggs ● Baseado e muito semelhante ao Joda-Time, porém melhor
  • 11. JSR 310: Date and Time ● Imutável e thread-safe ● Utilize sempre as classes mais específicas para o problema ● YearMonth - Mês e ano YearMonth.of(2013, Month.JULY); ● LocalDate - Data sem hora ou time-zone LocalDate.now(); LocalDate dataTDC = LocalDate.of(2013, Month.JULY, 12); ● LocalTime - Hora sem data ou time-zone LocalTime meiaNoite = LocalTime.MIDNIGHT; LocalTime onzeHoras = LocalTime.of(11, 0); assert meiaNoite.isBefore(onzeHoras);
  • 12. JSR 310: Date and Time ● LocalDateTime - Data com hora sem time-zone LocalDateTime dataTDCMeioDia = LocalDateTime. of(dataTDC, LocalTime.NOON); LocalDateTime dataTDCOnzeHoras = dataTDC.atTime(11, 0); assert dataTDCMeioDia. minusHours(1).equals(dataTDCOnzeHoras ); ● OffsetDateTime - Data com hora offset e sem time-zone OffsetDateTime. of(dataTDCMeioDia, ZoneOffset. ofHours(-3)); ● ZonedDateTime - Data com hora e time-zone ZonedDateTime. of(dataTDCMeioDia, ZoneId.of( "America/Sao_Paulo" ));
  • 13. JSR 310: Date and Time ● Outras classes de domínio ○ Year ○ Month - enum ○ DayOfWeek - enum ○ OffsetDate ○ OffsetTime ○ Period ○ Instant ○ Duration ○ Clock ● Nova API de formatação ● Diversos outros conceitos: ○ Temporals ○ Adjusters ○ Queries ○ Units
  • 14. JSR 335: Lambda Expressions ● Permite programação funcional, com maior nível de reutilização de código e escrita concisa int maiorIdadeDePessoaDoSexoMasculino = -1; for (Pessoa pessoa : pessoas) { if (pessoa.getSexo() == Sexo.MASCULINO) { int idade = pessoa.getIdade(); if (idade > maiorIdadeDePessoaDoSexoMasculino ) { maiorIdadeDePessoaDoSexoMasculino = idade; } } } if (maiorIdadeDePessoaDoSexoMasculino != -1) { trataIdade (maiorIdadeDePessoaDoSexoMasculino ); }
  • 15. JSR 335: Lambda Expressions ● Permite programação funcional, com maior nível de reutilização de código e escrita concisa pessoas.stream() .filter(pessoa -> pessoa.getSexo() == Sexo.MASCULINO) .mapToInt(Pessoa::getIdade) .max() .ifPresent(PessoaProcessor ::trataIdade);
  • 16. JSR 335: Lambda Expressions ● Permite programação funcional, com maior nível de reutilização de código e escrita concisa pessoas.parallelStream() .filter(pessoa -> pessoa.getSexo() == Sexo.MASCULINO) .mapToInt(Pessoa::getIdade) .max() .ifPresent(PessoaProcessor ::trataIdade);
  • 18. Migrando aplicações para Java SE 8 ● Foram migradas duas aplicações: ○ Um BI customizado para indústria automobilística com diversos gráficos e relatórios ○ Uma aplicação 24x7 que será lançada em breve ● Ambas com grande utilização do Guava, o que facilitou muito a migração para utilização de Lambda Expressions ○ Guava(code.google.com/p/guava-libraries) - Framework utilitário com suporte a programação funcional ● Forte utilização do Joda-Time, em especial o YearMonth por serem gráficos que acumulam dados estatísticos mensais ● Iniciamos há 8 meses e muita coisa vem sendo melhorada neste período
  • 19. O que podemos migrar automaticamente ● NetBeans 8 Nightly Builds está em desenvolvimento e já oferece algumas Hints para o Java SE 8 (Refactor > Inspect and Transform): ○ Hint: Convert to Lambda
  • 20. O que podemos migrar automaticamente ● NetBeans 8 Nightly Builds está em desenvolvimento e já oferece algumas Hints para o Java SE 8 (Refactor > Inspect and Transform): ○ Hint: Use Functions Operations
  • 21. O que podemos migrar automaticamente ● Benéfica principalmente para projetos Java SE ○ Usam mais Functional Interfaces (interfaces de um método abstrato apenas), boas candidatas à migração ○ Runnable, listeners do Swing etc. são exemplos ● Projetos Java EE só se beneficiarão mais se usarem alguma biblioteca funcional ○ Nós usamos :-)
  • 22. Tentando aprofundar o uso dos novos recursos ● Guava nos ajudou na migração automática, mas agora precisávamos eliminar para testar a API ● As operações funcionais mais comuns do Guava tem equivalente quase direto no Java SE 8: ○ filter -> filter ○ transform -> map ○ limit -> limit ● E o resto?
  • 23. Tentando aprofundar o uso dos novos recursos ● Como converter o resultado de uma operação funcional para uma List? List<String> names = brands.stream() .map(Brand::getName) .collect(toList()); ● Através de collectors (implementações padrão em Collectors) é que fazemos a maior parte das "terminal operations", i.e., converter de um stream para outra collection ou classe "sintetizadora" do resultado
  • 24. Tentando aprofundar o uso dos novos recursos ● Como gerar um Map<Long,Brand>? //Padrão throwingMerger: java.lang.IllegalStateException: Duplicate key Map<Long,Brand> brandById = brands.stream() .collect(toMap(Brand::getId, identity())); ● Mas e se houver colisões? ○ Um parâmetro adicional, quando especificado, define a estratégia de "merge": BinaryOperator<T> T apply(T u, T v) (u,v) -> u; //firstWinsMerger () método removido no b97 (u,v) -> v; //lastWinsMerger () método removido no b97
  • 25. Tentando aprofundar o uso dos novos recursos ● Mas e quando preciso de uma lista com colisões? Map<Holding,List<Brand>> brandByHolding = brands.stream() .collect(groupingBy(Brand::getHolding));
  • 26. Tentando aprofundar o uso dos novos recursos ● Como agregar elementos de uma coleção retornada pelo objeto do stream? List<Dealer> branches = dealers. stream() .flatMap(dealer -> dealer.getBranches().stream()) .collect(toList());
  • 27. Tentando aprofundar o uso dos novos recursos ● Novos métodos úteis em Map: //Java 7 Long l = totalByYearMonth.get(yearMonth); long total = l == null ? 0L : l; //Java 8 long total = totalByYearMonth. getOrDefault(yearMonth, 0L);
  • 28. Tentando aprofundar o uso dos novos recursos ● Novos métodos úteis em Map: //Java 7 Map<Brand,Long> totalByBrand = totalByBrandByYearMonth.get(yearMonth); if (totalByBrand == null) { totalByBrandByYearMonth.put(yearMonth, totalByBrand = new HashMap<>()); } Long t = totalByBrand.get(brand); totalByBrand.put(brand, t == null ? total : t + total); //Java 8 totalByBrandByYearMonth .putIfAbsent(yearMonth, new HashMap<>()) .merge(brand, total, Long::sum);
  • 29. Date and Time ● Formatador - por ser thread-safe é possível defini-lo em uma variável estática e utilizar em diversos ponto public static final DateTimeFormatter ANO_MES_FORMATTER = DateTimeFormatter. ofPattern("MMM/yyyy", new Locale("pt", "BR"));
  • 30. Date and Time ● A API prover diversos métodos e formas para que seja efetuado cálculos com data YearMonth start = YearMonth.now(); YearMonth end = YearMonth.now().plusMonths(1); int days = ChronoUnit.DAYS.between(start.atDay(1), end.atEndOfMonth()) .getDays();
  • 31. Date and Time ● Métodos para comparações YearMonth yearMonth = YearMonth.of(year, month); if (yearMonth.isAfter(YearMonth.now())) { //processa data futura... }
  • 32. Date and Time (Hibernate) ● Alguns lugares temos a persistência de LocalDate e LocalDateTime, como persistir com JPA(Hibernate)? UserType interface UserType { boolean isMutable();//não /** It is not necessary to copy immutable objects */ Object deepCopy (Object value); /** should perform a deep copy if the type is mutable */ Serializable disassemble (Object value); /** should perform a deep copy if the type is mutable */ Object assemble (Serializable cached, Object owner ); /** For immutable objects it is safe to simply return the first parameter */ Object replace (Object original, Object target, Object owner ); }
  • 33. Date and Time (Hibernate) ● Criar duas classes muito parecidas ou uma classe abstrata que implementa o comportamento parecido das duas? Nenhuma; default methods public interface ImmutableUserType extends UserType { @Override public default boolean equals(Object x, Object y) { return Objects.equals(x, y); } @Override public default int hashCode(Object x) { return Objects.hashCode(x); } @Override public default boolean isMutable() { return false; } ...
  • 34. Date and Time (Hibernate) ● E mais métodos: ... @Override public default Object deepCopy(Object value) { return value; } @Override public default Serializable disassemble(Object value) { return (Serializable) value; } @Override public default Object assemble(Serializable cached, Object owner) { return cached; } @Override public default Object replace(Object original, Object target, Object owner) { return original; } }
  • 35. ● LocalDateTimeType public class LocalDateTimeType implements ImmutableUserType { @Override public int[] sqlTypes() { return new int[] { Types.TIMESTAMP}; } @Override public Class<LocalDateTime> returnedClass () { return LocalDateTime. class;} @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) { Timestamp persistValue = (Timestamp) rs.getObject(names [0]); if (rs.wasNull()) { return null; } return persistValue. toLocalDateTime (); } ... Date and Time (Hibernate)
  • 36. ● LocalDateTimeType ... @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session ) { if (value == null) { st.setNull(index, sqlTypes ()[0]); } else { Timestamp timestamp = Timestamp .valueOf((LocalDateTime) value); st.setObject(index, timestamp, sqlTypes ()[0]); } } } Date and Time (Hibernate)
  • 40. Dificuldades ● Nossos estressados membros do EG, especialmente nosso amigo Brian, às vezes dão respostas "delicadas" ○ Porém ele pede desculpas em pvt depois, acreditem ou não :-) ● Nem sempre é muito fácil achar os métodos na API e precisa-se do apoio da lista ○ Pelo menos eles respondem muito rápido! ● Alguns métodos que mostramos que existem na API hoje foram resultados dessas discussões ○ Inclusive o getOrDefault, pro qual o Brian também deu uma resposta delicada de primeira, mas tá aí agora ● A API mudou de forma incompatível diversas vezes durante esse período, fazendo com que às vezes perdêssemos 1 dia inteiro só para deixar tudo recompilando com lambda de novo :-( ○ When you're living on the bleeding edge, you should not be surprised when you do, in fact, bleed
  • 41. Formatação e estilo ● A formatação e estilo do código afeta bastante a legibilidade (mais do que nunca): List<String> emailsOrdenados = pessoas.stream().filter((Pessoa pessoa) -> pessoa.getDataNascimento ().isBefore(dezAnosAtras)).map((Pessoa pessoa) -> pessoa.getEmail()).sorted((String o1, String o2) -> o1 .compareToIgnoreCase (o2)).collect(Collectors.toList()) ; ● Versus: List<String> emailsOrdenados = pessoas.stream() .filter(pessoa -> pessoa.getDataNascimento ().isBefore (dezAnosAtras)) .map(Pessoa::getEmail) .sorted(String::compareToIgnoreCase ) .collect(toList());
  • 42. Suporte a Stream de Maps
  • 43. Suporte a Stream de Maps NÃO TEM!
  • 44. Suporte a Stream de Maps ● Foi discutido pelo EG, mas descartado por exigir classes específicas e ser melhor suportado com tuplas ● Tínhamos na nossa base vários casos funcionais de Map com Guava e tivemos que converter para entrySet().stream() ● É tão feio que não daria tempo de vocês entenderem na palestra (é sério!) ● Vamos pensar seriamente se vale a pena manter na nossa base de código com Java SE 8
  • 45. Stream para array ● Como converter? ● Stream.toArray(IntFunction<A[]> generator) Pessoa[] p = pessoas.stream() //.filter .map ... .toArray((value) -> {//IntFunction > R apply(int value) //O que retornar? //new Pessoa[0] como em List.toArray?? //new Pessoa[10] acho que vai ter 10 ??? //new Pessoa[]{}; //java.lang.IndexOutOfBoundsException: does not fit });
  • 46. Stream para array ● Como converter? ● Stream.toArray(IntFunction<A[]> generator) Pessoa[] p = pessoas.stream() //.filter .map ... .toArray((value) -> { return new Pessoa[value]; });
  • 47. Stream para array ● Como converter? ● Stream.toArray(IntFunction<A[]> generator) Pessoa[] p = pessoas.stream() //.filter .map ... .toArray(Pessoa[]::new); //modo idiomático
  • 48. Acessos a recursos Java EE ● Algumas APIs Java EE, direta ou indiretamente, acreditam que podem controlar a instância "mágica" disponível via ThreadLocal (ex: FacesContext.getCurrentInstance()) ● Com Lambda, elas falham miseravelmente com parallelStream() ● Soluções? ○ Não usar parallelStream() :-( ○ Criar na thread principal e sair passando ○ Fazer patch do seu container preferido (se o seu container vem de uma empresa de 3 letrinhas, ele é todo baseado em threads pra isso... boa sorte!) ○ Perturbar o Brian na lista para que haja uma SPI de criação do mecanismo de execução de parallelStream() (Michael já cansou de fazer isso... boa sorte!) ○ Parar de brincar com tecnologias não suportadas oficialmente :-) ● Nós incluímos uma abstração no meio (porque o Janario tá com preguiça : -p)
  • 49. Problemas - Spring ● Spring(ASM) [SPR-10292] ○ O ASM não conseguia interpretar o bytecode gerado java.lang.IllegalArgumentException at org.springframework.asm.ClassReader.<init>(Unknown Source) ● Reportado pelo Michael em 13/02/2013 ● Solucionado 23/04/2013 ● Será lançado na versão 4.0 utilizamos em nossos testes a versão snapshot. ● Nestes meses continuamos nossa migração validando pelos testes de integração
  • 50. Problemas - Spring ● Spring - JDK (DocumentBuilderFactory(b92)) ○ No build 92 do JDK exista uma restrição de segurança que não permitia a requisição, durante a validação, de urls de namespace de xmls org.xml.sax.SAXException: schema_reference: Failed to read schema document 'spring-beans-3.1.xsd', because 'http' access is not allowed. ● Soluções: ○ System property -Djavax.xml.accessExternalSchema=all ○ Chamada via api DocumentBuilderFactory.setAttribute("http://javax. xml.XMLConstants/property/accessExternalSchema", "all"); ● Não ocorre mais na última versão testada b97
  • 51. Problemas - Spring ● Necessário utilizar o snapshot (enquanto não sair a versão final)
  • 52. Problemas - JBoss(Jandex) ● Jandex (Java Annotation Indexer) JANDEX-14 - Um indexador de anotações ○ Não conseguia interpretar classes com bytecode que continham expressões lambda (invokedynamic constant pool tag 18) java.lang.IllegalStateException: Unknown tag! pos=1 poolCount = 61 at org.jboss.jandex.Indexer.processConstantPool(Indexer.java:603) ● Reportado pelo Janario em 16/05/2013 ● Pull request aceito 22/05/2013 (https://github.com/wildfly/jandex/pull/12) - Janario Oliveira
  • 53. Problemas - JBoss x JDK ● ConcurrentSkipListSet - Ao adicionar os processors em um o mesmo fica com chamadas infinitas ao compareTo do objeto adicionado. org.jboss.as.server.deployment.RegisteredDeploymentUnitProcessor.compareTo(RegisteredDeploymentUnitProcessor.java:41) org.jboss.as.server.deployment.RegisteredDeploymentUnitProcessor.compareTo(RegisteredDeploymentUnitProcessor.java:28) java.util.concurrent.ConcurrentSkipListMap.findPredecessor(ConcurrentSkipListMap.java:696) java.util.concurrent.ConcurrentSkipListMap.doPut(ConcurrentSkipListMap.java:843) java.util.concurrent.ConcurrentSkipListMap.putIfAbsent(ConcurrentSkipListMap.java:2325) java.util.concurrent.ConcurrentSkipListSet.add(ConcurrentSkipListSet.java:241) org.jboss.as.server.DeployerChainAddHandler.addDeploymentProcessor(DeployerChainAddHandler.java:60) ● Não sabemos se é um bug no JDK ou no JBoss ● Utilizamos a versão customizada neste ponto em específico para evitar este erro.
  • 54. Problemas - Lombok ● Lombok ○ Issue #145 ainda em aberto desde 15/02/2013 :-( ○ Processor do Lombok não é compatível com o JavaC do Java SE 8 ○ Incompatível com NetBeans 7.4, já que o JavaC do Java SE 8 é utilizado pelo IDE para os parsings internos (Editor por exemplo) ● Reportado 15/02/2013 - Jan Lahoda ● Continua em aberto ● Apesar de não utilizarmos em nossos projetos, nosso amigo Michel Graciano utiliza.
  • 57. Microbenchmark - Caliper ● For each - AtomicInteger em uma lista //ForEachClassic for (Integer integer : list) { atomicInteger. accumulateAndGet (integer, Integer ::sum); } //ForEachStream list.stream().forEach((integer) -> { ... }); //ForEachArrayList list.forEach((integer) -> {...}); //ForEachParallelStream list.parallelStream ().forEach((integer) -> {...});
  • 58. Microbenchmark - Caliper For each - AtomicInteger
  • 59. Microbenchmark - Caliper For each - Fatorial em todos valores de 0 a 2000 private final IntFunction<Integer> factorial = i -> { return i == 0 ? 1 : i * factorial.apply(i - 1); }; //ForEachClassic for (Integer integer : list) { factorial. apply(integer); } //ForEachStream list.stream().forEach((integer) -> { ... }); //ForEachArrayList list.forEach((integer) -> { ... }); //ForEachParallelStream list.parallelStream ().forEach((integer) -> { ... });
  • 60. Microbenchmark - Caliper For each - Fatorial
  • 62. Conclusão ● Migrar aplicações do mundo real para o Java SE 8 hoje é possível - se você realmente souber Java e se elas tiverem testes ● As novas funcionalidades podem realmente tornar seu código bem mais legível ● Ganhos de performance podem ser obtidos - mas sempre meça seu código com ferramentas como Caliper, JMeter e um bom profiler ● Vários métodos e novos idiomas aceleram o desenvolvimento ● O Spring 4.0.0-SNAPSHOT *por enquanto* funciona, ao passo que o JBoss, só com hacks ● Para facilitar a sua migração use Java 7 (pra começo de conversa), adote o Guava e o backport da JSR 310 para Java 7 (https://github. com/ThreeTen/threetenbp) ● Siga as listas e participe ativamente das mesmas ● Se você acha que seria capaz de fazer as coisas descritas nessa palestra - e gostaria de ter tempo pago pela empresa para isso -, mande seu cv para recrutamento@tecsinapse.com.br :-)
  • 63. Obrigado! Janario Oliveira | @janarioliver Michael Nascimento Santos | @mr_ _m Michel Graciano | @mgraciano
  • 64. Agradecimentos ● Michel Graciano (@mgraciano) - Sem dúvida uma grande ajuda na coleta e organização do conteúdo destes slides
  • 65. Q&A Janario Oliveira | @janarioliver Michael Nascimento Santos | @mr_ _m Michel Graciano | @mgraciano