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

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

  • 1.
    Migrando aplicações do mundoreal para o Java SE 8 Janario Oliveira | @janarioliver Michael Nascimento Santos | @mr_ _m Michel Graciano | @mgraciano
  • 2.
    Apresentação ● Michael NascimentoSantos ○ 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 etestes foram realizados com o b97(30/06/2013) do Lambda
  • 7.
  • 8.
    Introdução rápida Introdução aosprincipais conceitos e tecnologias do Java SE 8
  • 9.
    JSR 337: JavaSE 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: Dateand 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: Dateand 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: Dateand 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: Dateand 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);
  • 17.
  • 18.
    Migrando aplicações para JavaSE 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 podemosmigrar 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 podemosmigrar 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 podemosmigrar 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 ouso 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 ouso 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 ouso 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 ouso 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 ouso 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 ouso 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 ouso 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 classLocalDateTimeType 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 voidnullSafeSet(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)
  • 37.
  • 38.
  • 39.
  • 40.
    Dificuldades ● Nossos estressadosmembros 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.
  • 43.
    Suporte a Streamde Maps NÃO TEM!
  • 44.
    Suporte a Streamde 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 recursosJava 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 - JBossx 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.
  • 55.
  • 56.
  • 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 Foreach - AtomicInteger
  • 59.
    Microbenchmark - Caliper Foreach - 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.
  • 61.
  • 62.
    Conclusão ● Migrar aplicaçõesdo 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