Friday, November 16, 2018

Hello World Java-Docker

Docker é um software que cria, gerencia e coordena containers. Containers por sua vez são processos que rodam dentro do Docker. Exemplos de containers são:
  • Wildfly JBoss 
  • MySQL
  • Linux Ubuntu
  • Ruby
  • Windows Nano
  • React NPM
  • JDK 6, 7, 8 etc.

Diferentemente dos processos que executam isoladamente em sistema operacional qualquer, os quais são compilados para executar somente naquele ambiente e ainda podem demandar outras instalações e configurações a parte, os containers são extremamente portáveis e rodam sobre uma imagem que representa o ambiente completo requirido para que aquele processo execute com sucesso.

Uma imagem no Docker é uma definição estática e imutável de todo um ambiente de execução.

Já o container é uma instância (em execução ou parada) de uma imagem. No Docker, containers estão para uma imagem assim como objetos estão para uma classe no Java.

Uma vez que containers não são instalados diretamente no sistema operacional local, nem suas dependências, que podem ser muitas, o maior benefíco do Docker é a portabilidade, já que um container pode executar em qualquer outro sistema operacional que tenha o Docker instalado.

O maior benefíco do uso de containers é a portabilidade

Hello World da Imagem Open JDK 10

Neste exemplo introdutório criamos uma classe chamada HelloJavaDocker que vai executar no container chamado hello-java-docker criado a partir da imagem Open JDK 10:

public class HelloJavaDocker{

    public static void main(String[] args){

        System.out.println("HELLO WORLD FROM JAVA DOCKER IMAGE!! *************\n\n");
    }
}

Compile a classe e gere o arquivo HelloJavaDocker.class:

$ javac HelloJavaDocker.java

No mesmo diretório no qual se encontra o arquivo HelloJavaDocker.class, crie um arquivo chamado Dockerfile. Este arquivo contém as instruções sobre como executar a classe HelloJavaDocker em qualquer lugar:

# imagem
FROM openjdk:10

# ADD recebe 2 parametros
# 1) fonte para adicionar na imagem; 2) localização do fonte dentro da imagem
ADD HelloJavaDocker.class .

# diz ao Docker como executar o fonte
ENTRYPOINT ["java", "HelloJavaDocker"]


Construindo a Imagem e Rodando o Container

Caso você não tenha o docker instalado na sua máquina, você pode baixá-lo, no Linux, pelo seguinte comando:
    
        $ wget -qO- https://get.docker.com/ | sh
    
Para versões a partir do Windows 10 (64bit) você pode baixar o executável na página oficial do Docker.

A maioria das operações no Docker se resumem a 2 passos:
  1. Construir a imagem, comando docker build
  2. Rodar o container, comando docker run

Para construir a imagem OpenJDK, vá para o diretório onde se encontra o arquivo Dockerfile e HelloJavaDocker.class e execute o seguinte comando:
    
        $ ls
        Dockerfile  HelloJavaDocker.class  HelloJavaDocker.java
        $ docker build -f Dockerfile -t hello-java-docker .
    

A opção -f  (file) recebe como argumnento a localização do Dockerfile.

A opção -t (tag) recebe como argumento o nome que daremos ao container e já cria uma instância baseada nessa imagem.

Agora execute a instância da imagem, que é o container hello-java-docker:
    
        $ docker run -it hello-java-docker
    


A classe HelloJavaDocker executou dentro do container hello-java-docker baseado na imagem Open JDK 10. 

Repare que nem sabemos se o Java está instalado e configurado no host local, pode ser sim, pode ser que não... Isso é indiferente! (na verdade o Java está instalado porque compilamos a classe fora da imagem, porém também poderíamos compilar o arquivo .java dentro do Docker)

Referências:
krammark23 Youtube
Docker Deep Dive, Nigel Poulton (2016)

       

Tuesday, October 16, 2018

Modular Projects no NetBeans 9.0

NetBeans 9 é a primeira release desde que a IDE passou a fazer parte da família Apache, cujo nome oficial agora é Apache NetBeans.

O NetBeans 9 também é a primeira versão desta IDE a conter suporte para o sistema de modularização da plataforma java, recurso que foi incluido a partir da versão 9 do java. Um módulo na plataforma Java consiste em:
  • Uma coleção de pacotes
  • Uma lista de quais pacotes estarão acessíveis para outros módulos
  • Uma lista de todos os módulos dos quais o módulo atual depende
Um módulo, portanto, pode controlar seletivamente quais pacotes estão disponíveis, isso reforça o encapsulamento e o gerenciamento de dependências, evitando problemas recorrentes na configuração do class path de seu projeto, tais como classes duplicadas ou não encontradas (ClassNotFoundException).



Java Modular Project

O NetBeans 9 apresenta uma nova opção de projeto, que é o Java Modular Project.
Caso queira modularizar sua aplicação, você deve escolher essa opção ao criar um novo projeto. 


Vamos criar um novo Java Modular Project que faz uso do módulo java.xml.bind, popularmente conhecido como JAXB API, ou seja, nosso projeto de exemplo converte uma classe java em um arquivo xml (você pode conferir todos os módulos disponíveis no javadoc do Java 10). Clique em Next e dê o nome ao novo projeto de JavaModular.

Clique com o botão direito na raiz do projeto JavaModular, navegue por new / module:


Dê o nome ao novo módulo de modular.test. (nomes de módulo devem ser globalmente únicos, a melhor forma de garantr isso é utilizar a mesma abordagem para nomeação de pacotes). O NetBeans já cria automaticamente o arquivo de configuração obrigatório em todos os módulos, chamado de module-info.java.

Clique com o botão direito no módulo que você acabou de criar e adicione um novo pacote chamado com:


Repare na estrutura do projeto. No pacote com vamos criar a classe Pais, que será convertida em um arquivo XML:
package com;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Pais {
 
 @XmlElement
 private String nome;
 @XmlElement
 private double area;

 public Pais() {
 }

 public Pais(String nome, double area) {
  this.nome = nome;
  this.area = area;
 }
    //getters e setters...

Ao criar as delcarações de import para os pacotes javax.xml, ocorre um erro de compilação, package javax.xml.bind.annotation is not visible. Isso porque esses pacotes estão em um módulo separado. Precisamos declarar explicitamente no arquivo module-info que nosso módulo modular.test precisa desses pacotes, os quais pertencem ao módulo java.xml.bind. Para isso utilizamos a palavra chave requires dentro do arqivo de configuração do módulo:

module modular.test{
 
    requires java.xml.bind;

}

Agora no pacote com crie a classe Main na qual instanciaremos um objeto da classe Pais:
package com;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class Main {

 public static void main(String[] args) throws JAXBException {
 
  Pais brasil = new Pais("Brasil", 8_600_000);
  JAXBContext context = JAXBContext.newInstance(Pais.class);
  Marshaller m = context.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  m.marshal(brasil, System.out);
 }
}

Até aqui vimos claramente como a modularização reforça o encapsulamento. Um módulo só pode acessar pacotes explicitamente exportados por outros módulos, ou seja, só conseguimos usar as classes do pacote java.xml.bind porque o módulo java.xml exporta tais pacotes. Por exemplo, se quisermos que o pacote que acabamos de criar com esteja disponível para uso em outros módulos, então no arquivo module-info adicionamos a declração exports para esse pacote:

 exports com;

Reflection API em Módulos

Outro ponto interessante a notar é que frameworks de mapeamento como o JAXB, que estamos usando neste exemplo, ou o JPA, que mapeia classes Java para tabelas no banco de dados, entre outros,  utilizam a API Reflection para fazer o mapeamento das propriedades das classes, mesmo que essas propriedades sejam privadas. Em um ambiente modular, tentar acessar campos privados via reflection não é 100% garantido. Sim, isso significa que um sistema desenvolvido no Java 8 (ou mais antigo) que faz uso de APIs como JAXB, JPA ou outros frameworks que utilizam extensivamente a API Reflection, pode não rodar no ambiente Java 9 (ou mais novo).

Tente executar a classe Main e comprove. Uma exceção é lançada porque o JAXB tenta acessar os campos privados da classe Pais via reflection.

Para liberar o acesso via reflection não basta exportar sua classe, é necessário utilizar a declaração opens para determinada classe ou pacote. O arquivo module-info portanto deve ficar como segue:
module modular.test {
    exports com;
    requires java.xml.bind;
    opens com;
}

Agora sim podemos executar a classe Main. Ela imprime na tela a o objeto Pais no formato xml:
<?xml version="1.0" encoding="UTF-8"?>
<pais>
    <nome>Brasil</nome>
    <area>8600000.0</area>
</pais>

         

Monday, October 1, 2018

AWS-EC2: Acessando Windows a partir do Linux

Após criar e lançar uma instância Windows na AWS, para acessá-lá a partir de um Linux, os seguintes passos são requeridos:

  1. Adicionar no security group da instância Windows uma regra interna (inbound rule) para permitir acesso remoto usando o protocolo RDP (Remote Desktop Protocol);
  2. Ainda no security group, liberar a origem do acesso remoto para seu próprio IP ou qualquer outro;
  3. Na máquina Linux instalar o software Remmina para o acesso remoto.

Configurando o Security Group

Por padrão, Security Groups não permitem acesso remoto pelo protocolo RDP. Você precisa criar uma regra interna liberando esse processo a partir de um ou qualquer endereço IP.

No console de administração da AWS, edite o Secutity Group da instância Windows para a qual você quer liberar o acesso remoto. Como na imagem abaixo, na combobox type selecione RDP e em source, qualquer IP. Salve esta configuração.



Instalando e configurando Remmina

Livre de licensas, Remmina é um cliente de desktop remoto para Linux semelhante ao TeamViewer. O Remmina suporta os protocolos  RDP, VNC, NX, XDMCP, SSH e Telepathy; ele também mantém a lista dos desktops remotos que você acessou.

Para instalar o Remmina execute
  sudo apt install remmina
    
Após instalar, abra o remmina e clique em new:



Na janela que vai abrir, você deve informar os dados de acesso da instância Windows:

  • Em protocol, selecione RDP
  • Em server, coloque o IP público da instância Windows e o número da porta 3389, no formato <numero_ip_publico>:3389
  • O nome do usário, geralmente Administrator
  • A senha de acesso
  • E o DNS público da instancia, algo como ec2-177-29-159-173.sa-east-1.compute.amazonaws.com


Na aba advanced, você encontra algumas opções Qualidade, Som. Qualidade de imagem menor, pode resultar em um ganho de velocidade. Se preferir, pode deixar como está:




Na aba SSH, mantenha desmarcada e opção Enable SSH tunel:


Dê um nome para sua conexão, no meu caso AWS Windows 2016, e salve-a. 

Agora selecione a conexão que você acabou de criar, botão direito do mouse, e clique em conectar:


O remmina exibe o desktop remoto com o qual você acabou de se conectar:



       

Tuesday, September 18, 2018

Microserviços com o PayaraMicro: parte 1

Microserviços constituem uma abordagem arquitetural para sistemas distribuíudos. O objetivo dessa arquitetura é promover o maior desacoplamento possível, de modo que cada serviço seja um sistema menor com seu próprio ciclo de vida, evitando assim armadilhas comuns em sistemas monolíticos maiores, tais como o aumento exponencial e descontrolado da complexidade e a consequente dificuldade de manutenção a medida em que novas funcionalidades vão sendo incorporadas. Empresas como Amazon e Netflix têm se destacado por promover e usar a arquitetura de microserviços em suas próprias plataformas. A comunicação entre os diferentes serviços geralmente é feita via web-services REST.

Microserviços no JavaEE

A iniciativa Eclipse MicroProfile  surgiu em 2016 e reúne grupos de usuários e as empresas que distribuem os principais containers JavaEE como WildFly (Red Hat), WebSphere (IBM), TomEE (TomiTribe) e Payara (antigo GlassFish). O objetivo da iniciativa é otimizar a plataforma JavaEE para o uso da arquitetura de microserviços, criando novos recursos e funcionalidades para isso.

A especificação Eclipse MicroProfile (atualmente na versão 2.0) define uma plataforma de tamanho reduzido para desenvolver microserviços utilizando as principais APIs JavaEE. Portabilidade, menos configuração e simplificação do processo de deploy também são um requisito. Todos os containers microprofile devem conter as seguintes especificações JavaEE:
  • JAX-RS 2.0
  • CDI 1.2
  • JSON-P 1.0 

Payara Micro


Neste artigo exploramos o Payara MicroProfile, o qual, como vimos, é compatível com a especificação Eclipse MicroProfile. Não confunda o Payara Micro com o Payara Server Full, que é o container tradiconal e maior, contendo toda API JavaEE bem como o console de administração.

O Payara Micro compõe um arquivo .jar de 70MB, não é necessário instalação, configuração, nem nenhuma escrita de código. Para subir o Payra Micro apenas execute o arquivo .jar passando como parâmetro o arquivo .war do serviço. Você pode baixá-lo aqui.

Como exemplo, vamos criar um serviço chamado BookService. Esse serviço acessa a base de dados e fornece uma lista de livros disponíveis para outra aplicação que acessa o serviço. Você pode escolher o banco de dados que mais lhe convier, neste exemplo utilizamos o MySQL. A IDE utilizada é o Eclipse JavaEE Photon.

O código fonte do projeto completo está disponível no github.

Crie um banco de dados chamado Livros.

Abra o Eclipse e crie um novo projeto MAVEN com o nome de BookService. No Arquivo pom.xml adicionamos as dependencias do mysql-connector e javaEE 8 API como segue:

<project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>BookService</groupId>
 <artifactId>BookService</artifactId>
 <version>1.0</version>
 <packaging>war</packaging>
 <properties>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 </properties>
 <dependencies>
  <dependency>
   <groupId>javax</groupId>
   <artifactId>javaee-web-api</artifactId>
   <version>8.0</version>
   <scope>provided</scope>
  </dependency>
  <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>8.0.12</version>
  </dependency>
 </dependencies>
</project>

Agora clique com o botão direito na pasta raiz do projeto. Navegue por Java EE Tools e depois selecione Generate Web Deployment Descriptor:


A importância do arquivo web.xml neste projeto será crucial. Tradicionalmente, aplicações Java EE definem o datasource no próprio servidor de aplicação seja via console admin ou por algum cliente em linha de comando. Com microserviços, convém utilizar uma outra variante. Vamos definir o datasource na própria aplicação e, mais ainda, podemos disponibilizar esse datasource para outros serviços. Isso é possível desde que usemos um JNDI válido para o datasource contendo o namespace java:global. Portanto nosso web.xml fica como segue:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
         version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <data-source>
        <name>java:global/livrosDataSource</name>
  <class-name>com.mysql.cj.jdbc.MysqlXADataSource</class-name>
        <server-name>localhost</server-name>
        <port-number>3306</port-number>
        <database-name>Livros</database-name>
        <user>root</user>        
        <password>km02h5</password>
        <property>
         <name>serverTimezone</name>
         <value>UTC</value>
        </property>
        <property>
         <name>useTimezone</name>
         <value>true</value>
        </property>
        <property>
         <name>useSSL</name>
         <value>true</value>
        </property>
        <property>
            <name>fish.payara.slow-query-threshold-in-seconds</name>
            <value>5</value>
        </property>
        <property>
            <name>fish.payara.log-jdbc-calls</name>
            <value>true</value>
        </property>
        <property>
            <name>fish.payara.is-connection-validation-required</name>
            <value>true</value>
        </property>
        <property>
            <name>fish.payara.connection-validation-method</name>
            <value>custom-validation</value>
        </property>
        <property>
            <name>fish.payara.validation-classname</name>
            <value>org.glassfish.api.jdbc.validation.MySQLConnectionValidation</value>
        </property>
    </data-source>
</web-app>

Como vamos utilizar as APIs Java Persistence (JPA) e Java Transaction (JTA), precisamos criar o arquivo persistence.xml e informar o nome do datasource criado no web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
 xmlns="http://xmlns.jcp.org/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

 <persistence-unit name="livrosDataSourcePU" transaction-type="JTA">
  <jta-data-source>java:global/livrosDataSource</jta-data-source>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
   <property
    name="javax.persistence.schema-generation.database.action"
    value="create" />
  </properties>
 </persistence-unit>
</persistence>

Crie agora 2 pacotes chamados com.app e outro com.entidades. No pacote entidades crie a classe Livro

//imports omitidos...

@Entity
@Table(name = "LIVROS")
@XmlRootElement
@NamedQuery(name = "Livro.buscarTodos", query = "SELECT l FROM Livro l")
public class Livro implements Serializable{
 
 private static final long serialVersionUID = 1L;

 @Id @Basic @NotNull
 private Long id;
 
 private String nome;
 
 private int numeroPaginas;
 
 private double preco;
 
 @Temporal(TemporalType.DATE)
 private Date publicacao;

    //getters, setters, equals e hashCode omitidos...
}

No pacote app configuramos o web service REST. Primeiro criamos a classe AplicationConfig. Nela definimos que os serviços poderão ser invocados quando houver a palavra rest na URL do  browser:

package com.app;

import java.util.Set;
import javax.ws.rs.core.Application;

@javax.ws.rs.ApplicationPath("rest")
public class ApplicationConfig extends Application{
 
 @Override
 public Set> getClasses() {
  Set> resources = new java.util.HashSet<>();
  resources.add(LivrosREST.class);
  return resources;
 }
}

E agora crie a classe LivrosREST, a qual será o web service propriamente dito, que retorna ao cliente os livros disponíveis:

//imports e package omitidos...

@Stateless
@Path("livros")
public class LivrosREST {

 @PersistenceContext(unitName = "livrosDataSourcePU")
 private EntityManager em;

 @GET
 @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
 public List findAll() {
  
  List livros = null;
  try {
   TypedQuery query = em.createNamedQuery("Livro.buscarTodos", Livro.class);
   livros = query.getResultList();
  }
  catch (NoResultException e) {
  }
  return livros;
 }
}

Nosso projeto já está pronto. Clique com o botão direito do mouse na pasta raiz do projeto, navegue pelas opções run/run as/maven install:


Após executar esse comando, repare que no log do eclipse aparece o caminho completo para onde foi gerado o arquivo .war. Selecione e copie esse caminho:


Agora vamos publicar o serviço. Vá para a pasta onde você salvou o arquivo jar do Payara Micro. Como foi dito no começo deste artigo, execute como um arquivo jar qualquer, e utilizamos o comando --deploy passando como parâmetro o arquivo .war que acabamos de gerar. Abra o prompt de comando e execute o seguinte comando:

java -jar  payara-micro-5.182.jar --deploy /home/rafael/eclipse-workspace/BookService/target/BookService-1.0.war

Ao final do log do servidor, aparece a URL do serviço publicado. A porta default é 8080:


Como a tabela do banco está vazia, ao acessar essa URL não teremos nenhum retorno. Como teste execute o seguinte script sql no seu banco de dados:

insert into LIVROS values (1, 'Pro Spring 5', 600, 25.99, '2010-05-25');
insert into LIVROS values (2, 'Java How to Program', 1200, 10.50, '1999-06-30');
insert into LIVROS values (3, 'JSP Design Patterns', 450, 1.99, '2001-11-28');
insert into LIVROS values (4, 'OCP Certification Guide', 900, 25.99, '2012-05-05');
insert into LIVROS values (5, 'Java EE 7', 600, 5.00, '2016-01-02');

Agora sim. Acesse novamente a URL http://localhost:8080/BookService-1.0/rest/livros e veremos a resposta sob a forma de um arquivo xml:


Na segunda parte deste post criamos uma aplicação cliente que acessa esse serviço.


       

Monday, August 20, 2018

Sorteado de Agosto Curso JEE JavaMail

O arquiteto Java Fernando Franzini tem sorteado cursos gratuitos de algumas APIs do Java EE no seu blog!


Friday, August 10, 2018

Layouts no JavaFX (parte 1)

JavaFX é uma API Java para construir aplicações com recursos avançados de interface gráfica com o usuário que rodam em uma variedade de dispositivos, desktop, mobile ou web.

Os componentes ou controles de interface gráfica podem ser os mais variados, como figuras geométricas, botões, tabelas, editores de texto, imagens, etc. Antes de iniciar a construção de uma tela, você precisa saber como irá dispor esses componentes na tela. Componentes indevidamente alinhados podem aumentar a curva de aprendizado do software e isso não é desejável em nenhum sistema.

Layout Managers

A forma como os controles de interface estão dispostos na janela é chamada de layout. A classes do JavaFX que servem de container para os demais controles da tela são chamadas de layout managers
e os controles nela incluídos são chamados de filhos ou children, eles podem ser adicionados ou removidos do container a qualquer momento. O que diferencia cada layout manager do outro é a forma como ele expões os children. Nessa primeira parte analisamos 5 layout managers: FlowPane, BorderPane, HBox, VBox e StackPane. Todos do pacote javafx.scene.layout.

FlowPane

FlowPane deixa os controles flutuarem ou se auto-ajustarem de acordo com os limites da janela ou do container. Por defualt, havendo espaço, os controles vão sendo expostos horizontalmente. Abaixo um código de exemplo de um FlowPane com três botões e dois radio buttons.
    
package _01flow;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class Main extends Application {

 public static void main(String[] args) {  
  launch(args);
 }

 @Override
 public void start(Stage primaryStage) throws Exception {

  ExemploFlowPane exemplo = new ExemploFlowPane();
  
  Scene scene = new Scene(exemplo.flowPane);  
  primaryStage.setScene(scene);
  primaryStage.setTitle("Exemplo Flow Pane");  
  primaryStage.setWidth(300);
  primaryStage.setHeight(200);
  primaryStage.show();    
 }

 public class ExemploFlowPane {

  public FlowPane flowPane;
  
  public ExemploFlowPane() {

   Button left = new Button("left");
   Button center = new Button("center");
   Button right = new Button("right");
   ToggleGroup group = new ToggleGroup();
   RadioButton rd1 = new RadioButton("option 1");   
   RadioButton rd2 = new RadioButton("option 2");
   rd1.setToggleGroup(group);
   rd2.setToggleGroup(group);

   flowPane = new FlowPane(8,8);
   flowPane.setAlignment(Pos.CENTER);   
   flowPane.getChildren().addAll(left, center, rd1, rd2, right);
   
   left.setOnAction(e -> flowPane.setAlignment(Pos.CENTER_LEFT));
   center.setOnAction(e -> flowPane.setAlignment(Pos.CENTER));
   right.setOnAction(e -> flowPane.setAlignment(Pos.CENTER_RIGHT));  
  }
 }
}
    


Ao redimensionar a janela, os controles mudam a disposição

Com FlowPane, os componentes são realinhados a medida que o espaço se altera.

BoderPane

BorderPane é um layout divido em cinco regiões: Norte, Sul, Oeste, Centro e Leste:


Assim, você só pode inserir os componente em uma dessas regiões. O exemplo abaixo coloca um botão em cada uma das regiões. Em cada botão é registrado o evento de desaparecer quando clicado, deixando vazio o espaço que ele ocupa no BorderPane:
    
package _02borderPane;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Main extends Application {

 public static void main(String[] args) {
  launch(args);
 }
 
 @Override
 public void start(Stage primaryStage) throws Exception {
 
  ExempĺoBorderPane exemplo = new ExempĺoBorderPane();
  Scene scene = new Scene(exemplo.borderPane);  
  primaryStage.setScene(scene);
  primaryStage.setTitle("Exemplo Border Pane");  
  primaryStage.setWidth(500);
  primaryStage.setHeight(500);
  primaryStage.show(); 
 }
 
 public class ExempĺoBorderPane implements EventHandler{
  
  public BorderPane borderPane = new BorderPane();
  String[] names = {"Hide North", "Hide South", "Hide East", "Hide West", "Hide Center"};
  Button[] buttons = new Button[5];
    
  public ExempĺoBorderPane() {

   for(int i = 0; i < names.length; i++) {
    buttons[i] = new Button(names[i]);
    buttons[i].setOnAction(this);
    buttons[i].setMaxHeight(Double.MAX_VALUE);
    buttons[i].setMaxWidth(Double.MAX_VALUE);
   }
   borderPane.setTop(buttons[0]);
   borderPane.setLeft(buttons[3]);
   borderPane.setCenter(buttons[4]);
   borderPane.setRight(buttons[2]);
   borderPane.setBottom(buttons[1]);
  }

  @Override
  public void handle(ActionEvent event) {

   for(Button button : buttons) {
    
    if(event.getSource() == button)
     button.setVisible(false);
    else
     button.setVisible(true);
   }
  }
 }
}
    

O espaço central vazio

HBox


O Layout HBox alinha os controles em uma linha horizontal ilimitada. No exemplo a seguir o evento do botão vai adicionando novos botões no HBox inicialmente vazia:

package _03HBox;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class Main extends Application{

 public static void main(String[] args){
  launch(args);
 }

 @Override
 public void start(Stage primaryStage) throws Exception {

  ExemploHBox exemplo = new ExemploHBox();
  Scene scene = new Scene(exemplo.root);  
  primaryStage.setScene(scene);
  primaryStage.setTitle("Exemplo HBox");  
  primaryStage.setWidth(800);
  primaryStage.setHeight(200);
  primaryStage.show();
 }
 
 public class ExemploHBox{
    
  public HBox hBox;
  public BorderPane root;
  Button add;
  
  public ExemploHBox() {
  
   hBox = new HBox(5);
   root = new BorderPane();
   add = new Button("Add to Box");
   add.setOnAction(e -> hBox.getChildren().add(new Button("new element")));   
   root.setBottom(add);
   root.setCenter(hBox);   
  }
 }
}    




VBox

O layout VBox é como o HBox, porém com alinhamento verttical dos controles. No exemplo anterior, susbtitua HBox por VBox e o resultado é o que segue:


StackPane

StackPane empilha os controles em uma estrutura LIFO (Last In First Out), último que entra é o primeiro que sai. Dessa forma o último controle inserido é o que fica no topo. Geralemtne no uso de StackPane somente controle no topo deve ficar visível. No exemplo abaixo empilhamos no StackPane todos os layouts criados anteriormente, de modo que só o que está no topo fica visível. O botão next dispara um evento que manda o primeiro elemento da pilha para o final, deixando o próximo visível:
    
package _05StackPane;

import _01flow.Main.ExemploFlowPane;
import _02borderPane.Main.ExempĺoBorderPane;
import _03HBox.Main.ExemploHBox;
import _04VBox.Main.ExemploVBox;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {

 @Override
 public void start(Stage primaryStage) throws Exception {
  
  ExemploStackPane stackPane = new ExemploStackPane();
  Scene scene = new Scene(stackPane.boderPane);
  primaryStage.setScene(scene);
  primaryStage.setTitle("Stack Pane");
  primaryStage.setWidth(600);
  primaryStage.setHeight(600);
  primaryStage.show();
  
 }

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  launch(args);
 }
 
 class ExemploStackPane{
  
  StackPane stackPane;
  BorderPane boderPane = new BorderPane();

  public ExemploStackPane() {
  
   stackPane = new StackPane();
   ExemploFlowPane exemploFlowPane = new _01flow.Main().new ExemploFlowPane();
   ExempĺoBorderPane exempĺoBorderPane = new _02borderPane.Main().new ExempĺoBorderPane();
   ExemploHBox exemploHBox = new _03HBox.Main().new ExemploHBox();
   ExemploVBox exemploVBox = new _04VBox.Main().new ExemploVBox();
   
   stackPane.getChildren().add(exemploFlowPane.flowPane);
   stackPane.getChildren().add(exempĺoBorderPane.borderPane);
   stackPane.getChildren().add(exemploHBox.root);
   stackPane.getChildren().add(exemploVBox.root);   
   
   stackPane.getChildren().forEach(node -> node.setVisible(false));
   
   Button button = new Button("next");
   button.setOnAction(e -> {
    ObservableList list = stackPane.getChildren();
    Node top = list.get(list.size() - 1);
    Node newTop = list.get(list.size() - 2);    
    top.setVisible(false);
    top.toBack();    
    newTop.setVisible(true);    
   });
   
   boderPane.setCenter(stackPane);
   boderPane.setBottom(button);
  } 
 }
}
    



Na segunda parte deste post, analisamos o AnchorPane, GridPane, TilePane e BorderImage.

       

Friday, August 3, 2018

É hora de se tornar um Oracle Certified Professional!

Oracle Certified Professional (OCP) é a segunda cerftificação oficial que profissionais que trablham com Java podem tirar. A primeira é a Oracle Certified Associate (OCA). Se você quiser saber mais detalhes sobre a OCA, como estudar, agendar a prova e  obtê-la rapidamente, veja Obtendo sua OCA (Oracle Certified Associate Java SE 8) em 60 dias.

Oracle Certified Professional (OCP)

 Acabei de prestar o exame 1ZO-809, que é o exame sob o qual você se submete a fim de se tornar um Oracle Certified Professional em Java. Felizmente passei com 70%, o score mínimo para passar é 65%. Neste post dou meu feedback para você também passar no exame.



A OCP é uma continuação da OCA, portanto os criadores do exame partem da premissa de que todos os tópicos cobrados na OCA você já os tem dominados. Dessa forma, o exame da OCP inclui indiretamente todos os tópicos do exame anterior, mais uma série de novos objetivos, sobre a maioria dos quais é cobrado conhecimento profundo, principalmente sobre as classes dos novos pacotes incluídos a partir da versão 8 do Java, java.util.function e java.util.stream. Principais tópicos cobrados no exame 1ZO-809:
  • API de concorrência (Thread, Runnable, Callable, Executors, etc.)
  • Programação Funcional
  • Streams API
  • Java I/O
  • Java NIO
  • Generics e Collections
  • Design Patterns e Design Principles
Todo processo de agendamento do exame 1ZO-809 é idêntico ao da OCA (inclusive o preço), portanto não vou abordar essa parte. Para saber mais detalhes sobre o agendamento, confira
no link acima que contem o meu feedback sobre a OCA. Este post foca mais no conteúdo do exame OCP e métodos de estudo.

Até a data deste post, o Java estava na versão 11, de acordo com a nova estratégia da Oracle de lançar uma nova versão a cada 6 meses. No entanto, os exames de certificação disponíveis contemplavam somente até a versão 8. Isso não é um problema porque as diferença entre os recursos criados a partir do Java 8 até o 11 não são tão significativas quanto as mudanças introduzidas entre o Java 7 e 8. Outrossim ainda que a Oracle disponibilizasse amanhã um novo exame de certificação para a versão 10 ou 11, não seria recomendável fazê-lo logo de cara, porque você praticamente não terá nenhum sólido material de apoio como livros, fóruns, vídeos, etc. O exame da OCP 8 já existe há mais de 4 anos, muita gente já fez, muitos reprovaram, muitos passaram, há, portanto, bastante feedback e o material de apoio disponível para quem quiser encarar o desafio de obter a OCP é muito grande e diversificado. Este post é mais um.


Dificuldade do Exame

Indo direto ao ponto, o exame da OCP é muito mais difícil que o da OCA. Para passar, você precisará comer Java. Beber Java. Respirar Java. E - principalmente - pensar em Java. Seguindo essas dicas, o exame se torna fácil!

Sendo mais específico, o exame possui apenas 2 grandes dificuldades, as quais, você superando, tem grandes chances de ser bem sucedido. Superá-las é simples, mas não fácil.

Diferentemente da OCA, acredito não ser possível ser bem sucedido nesse exame se você não trabalha com Java. Esse é o ponto de partida. Você precisa de um nível de familiaridade com Java que dificilmente é obtido caso você não utilize a  tecnologia no dia-a-dia. Essa familiaridade é importante porque, não bastasse o profundo conhecimento exigido sobre cada tópico, os criadores do exame também são famosos por trapacear nas questões. Refiro me as famosas pegadinhas. Elas estão por toda parte. Essa é a primeira grande dificuldade a que me referi. Veja este exemplo: O que o código abaixo exibe na tela?

    Stream<Integer> prime = Stream.of(2,3,5,7);
    Stream<Integer> composite = Stream.of(4,6,8);
    ConcurrentMap<Boolean, List<Integer>> data =
          Stream.combine(prime, composite).parallelStream()
              .flatMap(s -> s)                        
              .collect(Collectors.groupingByConcurrent(n -> (n % 2) == 0));
        
    System.out.println(data.get(true).size()+" "+data.get(false).size());
Parece ser uma questão sobre parallel reduction, mas na verdade o código nem compila. A classe Stream não tem o método combine() e para criar um parallel stream a partir de um stream convencional você deve chamar o método parallel(), o método parallelStream() não existe.

Para não cair nesses truques, uma habilidade que você precisa treinar muito é se acostumar a ler códigos, de preferência sem formatação, e descobrir onde estão os erros de compilação. Claro, isso só depois de estar bem familiarizado com a API em questão. Assim você supera a primeira grande dificudade.
A segunda grande dificuldade é a quantidade muito extensa de tópicos cobrados no exame e o conhecimento profundo exigido sobre cada um. Superar essas dificuldade também é simples. Você precisa ter uma estratégia e uma metodologia de estudo, caso contrário ficará patinando, vai passar um ano, e você ainda não estará pronto para o exame! Compartilho minha estratégia de estudo.


Método de estudo

Comecei a estudar em 19/Março e fiz a prova em 31/7. Pouco mais de 4 meses de preparação. Acredito que não mais do que 6 meses de estudo para esse tipo de exame é um tempo aceitável.

1 - Escolha o Material de Apoio

A primeira coisa que você deve fazer é escolher seu material de apoio. Existem alguns livros no mercado, mas recomendo fortemente o OCP Study Guide de Jeanne Boyarsky e‎ Scott Selikoff da editora Sybex:


O livro é bem organizado e cobre 100% do conteúdo da prova. Os autores são didáticos, mas não fazem milagres. Você vai descobrir que é impossível ser didático tentando ensinar, por exemplo, o Fork/Join Framework ou Parallel Reductions. É o tipo de assunto que é o aluno quem aprende e não o professor quem ensina. Cada capítulo do livro possui cerca de 20 exercícios relacionados. O site da editora Sybex também tem 3 simulados com 60 questões cada um e repostas comentadas. Uma ferramenta valiosíssima da qual você não deveria abrir mão!


2 - Metodologia

Existe uma ciência do estudo. Muita coisa já foi publicada sobre como estudar de forma rápida e eficiente. Existem dezenas de técnicas que comprovadamente funcionam. Um livro que me deu vários insights foi Como passar no vestibular do dr. Lair Ribeiro. Utilizei as técnicas de anotação, comparação de abordagens, leitura skimming ou dinâmica, leitura analítica e mapas mentais.

Com uma caneta marca texto, primeiro faça uma leitura dinâmica sobre o capítulo. O objetivo nessa leitura não é entender tudo, mas se familiarizar com o assunto. A medida que for lendo, marque os trechos, imagens e tabelas que considerar importantes:

Marcando os trechos mais importantes para posterior releitura

Ao terminar a leitura dinâmica, recomece, agora com a leitura analítica, mais lenta e detalhada. A medida em que você prossegue coma leitura analítica, vá elaborando os mapas mentais (o software que usei foi o MindMUp 2.0 para o Google Drive), no mínimo cada mapa mental deve conter todas as subseções do capítulo em questão (mas essa regra pode ser quebrada, depende de cada um...). Mapas mentais são muito úteis porque o cerebro memoriza mais facilmente estruturas hierarquicas e coloridas. Há vários estudos demonstrando isso.


Exemplo de Mapa Mental sobre a API I/O

Durante a leitura analítica, implemente os vários códigos de exemplo e faça experiências.

Utilize a técnica de comparação de abordagens. O cerebro também memoriza mais facilmente quando você tem acesso a 2, 3 ou quatro abordagens diferentes sobre o mesmo assunto. Utilize essa técnica somente nos assuntos em que tiver mais dificuldade, se usar em todos, vai perder muito tempo. Utilizei a comparação de abordagens principalmente nos tópicos que cobrem Concurrency API e Stream API, para este último utilizei os capítulos 4 e 5 do livro Java 8 in Action de Raoul Urma, Mario Fusco e Alan Mycroft da editora Manning. Com relação a API Stream o livro Java 8 in Action possui uma abordagem muito mais interessante do que a presente em OCP Study Guide. Mas creio que as ambas se complementam:



Quando estiver mais ou menos na metade da leitura analítica de um capítulo, comece a fazer a leitura dinâmica do próximo capítulo. Prossiga assim até o final.


Revisões

Releia de forma periódica todos os trechos marcados e os mapas mentais. Revise a estrutura do mapa mental alguns dias depois de terminá-lo, acrescente ou elimine nós se achar necessário. A grande vantagem dos mapas mentais é que eles permitem que você estude uma grande quantidade de tópicos de forma rápida e ainda memorizá-los mais facilmente.

Optei por fazer os exercícios do livro e os simulados na última semana, somente depois de terminar todos os capitulos.


Por que tirar uma certficação?

Certificação oficial é uma vantagem enorme no seu currículo. Quem pensa o contrário, com certeza não tem nenhuma.

Uma certificação oficial significa que a empresa realizadora do exame (geralmente líder no seu segmento de mercado) assina embaixo que você possui todas as credenciais mencionadas. Por exemplo, a chefe de um RH pode não entender nada de Java e outras tecnologias em detalhe, mas com certeza ela conhece as marcas Oracle, Microsoft, IBM, CISCO, Amazon etc. Quando você tem a certificação, essas marcas passam a recomendar você!

Outro aspecto interessante, é que ao passar no exame você ganha um certificado em forma de diploma, geralmente assinado por algum executivo de alto escalão da empresa. Antigamente os certificados da Microsoft vinham com a assinatura de ninguém menos que Bill Gates!


Outro recurso bacana é que você ganha medalhas. E pode vinculá-las à URL que atesta sua certificação. Nessa URL qualquer pessoa no mundo inteiro pode checar as competências cobradas no exame que você passou. É um diferencial profissional e tanto!


Os exames são difíceis e isso é bom porque limita a oferta de certificados no mercado. Outra mensagem implícita nos certificados é que você é um profissional que encara desafios. Muita gente competente tem condições de passar, mas não está disposta a se arriscar. O mero agendamento do exame tem um custo muito alto. Se não passar, você simplesmente perde todo dinheiro. Não há absolutamente nenhuma garantia, exceto a sua dedicação em se preparar para o exame.

E aí vai encarar?