top of page
java aplic.jpg

Desenvolvimento

de                

aplicações

gráficas

A linguagem Java oferece, dentre as funcionalidades incorporadas à sua API padrão, um extenso conjunto de classes e interfaces para o desenvolvimento de aplicações gráficas. Esse conjunto facilita a criação de saídas na forma gráfica e de interfaces gráficas com usuários (GUIs), tanto na forma de aplicações autônomas como na forma de applets.

Aplicações gráficas são criadas através da utilização de componentes gráficos, que estão agrupa- dos em dois grandes pacotes: java.awt e javax.swing.

AWT é o Abstract Windowing Toolkit, sendo definido através das classes do pacote java.awt e seus subpacotes, tais como java.awt.event. Essas classes agrupam as funcionalidades gráficas que estão presentes desde a primeira versão de Java, que operam tendo por base as funcionalidades de alguma biblioteca gráfica do sistema onde a aplicação é executada.

Já o pacote javax.swing define uma extensão padronizada a partir de AWT que congrega componentes gráficos que utilizam exclusivamente Java (lightweight components), com funcionali- dades e aparência independentes do sistema onde a aplicação é executada.

 

4.1 Apresentação gráfica

A base para tudo que é desenhado na tela de uma aplicação gráfica é o contexto gráfico, em Java um objeto da classe Graphics do pacote java.awt. São nesses objetos que linhas, retângulos, círculos, strings, etc. são desenhados, compondo a apresentação visual da aplicação gráfica.

Todo componente gráfico tem associado a si um contexto gráfico e alguns métodos chaves para manipulá-lo. A um objeto da classe Component pode-se aplicar os métodos getGraphics(), que permite obter uma referência para um objeto da classe Graphics; paint(Graphics), que determina o que é feito no contexto gráfico; e repaint(), que solicita uma atualização no contexto gráfico.

Para determinar o conteúdo gráfico do contexto, os métodos da classe Graphics são utilizados. Esses métodos incluem facilidades para definição de cores, fontes e definição de figuras geométricas. O sistema de coordenadas para definir a posição do desenho no contexto tem origem no canto superior esquerdo da área gráfica, tomando valores inteiros para e representando quantidades de pixels na horizontal e na vertical, respectivamente.

A cor corrente para o contexto gráfico pode ser obtida e definida pelos métodos getColor()

e setColor(), respectivamente. Esses métodos usam cores representadas por objetos da classe

Color, que oferece constantes para usar cores pré-definidas, métodos para definir cores especifican- do níveis RGB (red, green, blue) ou HSB (hue, saturation, brightness) e métodos para manipular as cores existentes.

Similarmente, a fonte usada para a apresentação de strings no contexto pode ser obtida e definida através de métodos getFont() e setFont(). Fontes são representadas por objetos da classe Font.

Entre os diversos métodos usados para desenhar no contexto, há métodos para desenhar texto usando a fonte de texto e a cor corrente, drawString(); desenhar linhas retas, drawLine(); e desenhar diversas formas geométricas, tais como arcos, drawArc(); retângulos, drawRect(), drawRoundRect(), draw3DRect(); polígonos,  drawPolygon(); ovais,  drawOval(); e suas correspondentes versões preenchidas, fillArc(), fillRect(), fillRoundRect(), fill3DRect(), fillPolygon() e fillOval().

 

4.1 Interfaces gráficas com usuários

Criar uma interface gráfica com usuários em Java envolve tipicamente a criação de um container, um componente que pode receber outros. Em uma aplicação gráfica autônoma, tipicamente o con- tainer usado como base é um frame (uma janela com bordas e barra de título), enquanto que em um applet o container base é um panel (uma área gráfica inserida em outra). Em aplicações complexas, qualquer tipo de combinação desses e de outros tipos de componentes é utilizado.

Após sua criação, os containers da aplicação recebem componentes de interface com usuários, que permitirão apresentar e receber dados aos e dos usuários. Embora seja possível posicionar esses componentes através de coordenadas absolutas em cada container, é recomendável que o projetista utilize sempre um gerenciador de layout para realizar essa tarefa. Deste modo, garante-se que a apli- cação possa ser executada independentemente das características da plataforma onde será executada.

Finalmente, é preciso especificar quais devem ser os efeitos das ações dos usuários — tais como um "clique"do mouse ou uma entrada de texto — quando realizadas sobre cada um desses compo- nentes. Isto se dá através da especificação de classes manipuladoras de eventos, projetadas para a aplicação. Ao associar objetos dessas classes aos componentes gráficos, o projetista determina o comportamento da aplicação.

 

4.1.1 Eventos da interface gráfica

No modelo de eventos suportado a partir da versão 1.1 de Java, um componente gráfico qual- quer pode reconhecer alguma ação do usuário e a partir dela disparar um evento — indicando, por exemplo, que o botão do mouse foi pressionado ou que um texto foi modificado — que será captu- rado por um objeto registrado especificamente para registrar aquele tipo de evento ocorrido naquele componente.

O pacote java.awt.event define as diversas classes de eventos que podem ocorrer através das interfaces gráficas. Eventos gráficos são objetos derivados da classe AWTEvent, que por sua vez são derivados de objetos de evento genéricos definidos pela classe EventObject. Desta, even- tos gráficos herdam o método getSource(), que permite identificar o objeto que deu origem ao evento.

Eventos gráficos genéricos são especializados de acordo com o tipo de evento sob consideração

— por exemplo, pressionar um botão do mouse gera um objeto da classe MouseEvent. A mesma ação sobre um botão, no entanto, gera também um ActionEvent, enquanto que sobre o botão de minimizar na barra de título de um frame um WindowEvent seria ao invés adicionalmente gerado. Outros eventos de interesse definidos em java.awt.event incluem ItemEvent, indicando que um item de uma lista de opções foi selecionado; TextEvent, quando o conteúdo de um componente de texto foi modificado; e KeyEvent, quando alguma tecla foi pressionada no teclado.

 

A resposta que uma aplicação dá a qualquer evento que ocorre em algum componente gráfico é determinada por métodos específicos, registrados para responder a cada evento. O nome e a assina- tura de cada um desses métodos é determinado por uma interface Java do tipo listener. Assim, para responder a um ActionEvent será utilizado o método definido na interface ActionListener; para responder a um WindowEvent, os métodos de WindowListener. Similarmente, há interfa- ces ItemListener, TextListener e KeyListener. Os eventos do mouse são manipulados através de métodos especificados em duas interfaces, MouseListener (para ações sobre o mouse)

e MouseMotionListener (para tratar movimentos realizados com o mouse).

Apesar de existir um grande número de eventos e possibilidades de resposta que a aplicação poderia dar a cada um deles, cada aplicação pode especificar apenas aqueles para os quais há interesse que sejam tratados. Para os eventos que o projetista da aplicação tem interesse de oferecer uma reação, ele deve definir classes manipuladoras de eventos (handlers), implementações de cada listener correspondente ao tipo de evento de interesse.

Para interfaces listener que especificam diversos métodos, classes adaptadoras são definidas. Essas classes adaptadoras são classes abstratas definidas no pacote de eventos AWT, com nome XX- XAdapter, onde XXX seria o prefixo correspondente ao tipo de evento de interesse. Assim, para que a aplicação use uma classe adaptadora para tratar os eventos do tipo WindowEvent, por exem- plo, uma classe que estende a classe WindowAdapter pode ser definida. Nessa classe derivada, os métodos relacionados aos eventos de interesse são redefinidos (pelo mecanismo de overriding). Co- mo a classe adaptadora implementa a interface correspondente WindowListener, assim o fará a classe derivada. Os métodos não redefinidos herdarão a definição original, que simplesmente ignora os demais eventos.

A API de eventos AWT de Java define, além de WindowAdapter, as classes adaptadoras Mou- seAdapter, MouseMotionAdapter e KeyAdapter. Uma vez que uma classe handler tenha sido criada para responder aos eventos de um compo- nente, é preciso associar esse componente ao objeto handler. Para tanto, cada tipo de componente gráfico oferece métodos na forma addXXXListener(XXXListener l) e removeXXXLis- tener(XXXListener l), onde XXX está associado a algum evento do tipo XXXEvent. Por exemplo, para o componente Window (e por herança para todas os componentes derivados des- se) são definidos os métodos public void addWindowListener(WindowListener l) e public void removeWindowListener(WindowListener l), uma vez que nesse ti- po de componente é possível ocorrer um WindowEvent.

 

Manipuladores de eventos de baixo nível

Eventos de mouse são tratados pelos métodos definidos em dois listeners distintos definidos em

java.awt.event. A interface MouseListener especifica os métodos para os eventos de maior

destaque na interação do usuário com o mouse. A classe abstrata MouseAdapter oferece imple- mentações vazias para todos esses métodos.

Um objeto associado a um evento do mouse descreve eventos notáveis ocorridos com o mouse, como o fato de ter entrado ou saído na área de interesse (uma janela, tipicamente), um “clique” ocorrido, se alguma tecla de modificação (ALT, SHIFT ou CONTROL) estava pressionada e a posição na qual o evento ocorreu.

A interface MouseMotionListener especifica métodos para manipular eventos de movi- mentação do mouse. A classe abstrata MouseMotionAdapter é uma adaptadora para esse liste- ner. Nesse caso, qualquer movimentação do mouse é relatada, diferenciando entre eventos da forma MOUSE_MOVED, para movimentos com o mouse livre, e MOUSE_DRAGGED, para movimentos do mouse com algum botão pressionado.

A interface KeyListener especifica os métodos necessários para detectar e tratar eventos de teclado. São eles: keyPressed(), para indicar que a tecla foi pressionada; keyReleased(), para indicar que a tecla foi solta; e keyTyped(), para indicar que uma tecla que não de controle foi pressionada e solta. Todos os métodos recebem um objeto KeyEvent como parâmetro. Esse objeto indica o tipo de evento ocorrido (KEY_PRESSED, KEY_TYPED ou KEY_RELEASED), o código da tecla e se havia modificadores associados. Como essa interface listener especifica mais de um método, uma classe adaptadora, KeyAdapter, é oferecida.

 

Manipuladores de eventos de alto nível

Raramente uma aplicação está preocupada em tratar eventos no nível tão básico como qualquer movimento do mouse ou tecla pressionada. Java oferece um conjunto de funcionalidades que filtram esses eventos para outros mais próximos da visão da aplicação, como o pressionar de um botão ou entrar texto em um campo.

A interface ActionListener especifica o método actionPerformed() para tratar even- tos do tipo ação. Objetos da classe ActionEvent representam eventos associados a uma ação aplicada a algum componente AWT.

Uma descrição (na forma de uma string) da ação pode ser obtida através do método getAc- tionCommand(). Dessa forma, se mais de um componente compartilha um mesmo listener, esse método permite diferenciar qual componente foi o gerador da ação.

Outro atributo que está representado em um objeto evento diz respeito aos modificadores, teclas que podem estar apertadas simultaneamente à ocorrência do evento para requisitar um tratamento alternativo. A classe ActionEvent define o método getModifiers(), que retorna um valor inteiro que pode ser analisado em relação a quatro constantes da classe, ALT_MASK, CTRL_MASK, META_MASK e SHIFT_MASK.

Um objeto de uma classe que implemente ActionListener deve ser associado a um com-

ponente através do método addActionListener(), que é definido para componentes do tipo botão, listas e campos de texto.

Quando um evento desse tipo ocorre, o objeto registrado é notificado. O método actionPer- formed() é então invocado, recebendo como argumento o objeto que representa o evento.

Eventos associados à manipulação de janelas são definidos na interface WindowListener.

 

Essa interface especifica os seguintes métodos:

 

windowOpened() trata evento que é apenas gerado quando a janela é criada e aberta pela primeira vez

windowClosing() usuário solicitou que a janela fosse fechada, seja através de um menu do sistema ou através de um botão da barra de títulos ou através de uma sequência de teclas do sistema.

windowClosed() trata evento que indica que a janela foi fechada. windowIconified() trata evento que indica que janela foi iconificada. windowDeiconified() trata evento indicando que o ícone da janela foi aberto.

windowActivated() trata evento que indica que a janela ganhou o foco do teclado e tornou-se a janela ativa.

windowDeactivated() trata evento que indica que a janela deixou de ser a janela ativa, provavel- mente porque outra janela foi ativada.

Todos esses métodos têm como argumento um objeto da classe WindowEvent, que representa um evento de janela. Como a interface WindowListener especifica diversos métodos, uma classe adaptadora abs- trata, WindowAdapter, é fornecida com implementações vazias de todos os métodos.

Para implementar a interface ItemListener é preciso definir a implementação do método itemStateChanged(), que recebe como argumento um objeto evento do tipo ItemEvent. Um ItemEvent sinaliza que um elemento de algum tipo de lista de opções foi selecionado ou desselecionado. Componentes que emitem esse tipo de evento implementam a interface ItemSelectable.

A interface TextListener especifica o método que trata eventos do tipo texto. Um obje-  to da classe TextEvent está relacionado a um evento indicando que o usuário modificou o valor de texto que aparece em um TextComponent — qualquer modificação no texto apresentado no componente gera esse evento. Um método apenas é especificado na interface, textValueChan- ged(TextEvent e). O corpo desse método determina o que deve ser executado quando o texto em algum componente de texto é alterado.

 

4.1.1 Componentes gráficos

A criação de uma interface gráfica com usuários utiliza tipicamente uma série de componentes pré-estabelecidos para tal fim, tais como rótulos (labels, textos que são apresentados mas não alterá- veis) e botões, um componente que pode ser ativado pelo usuário através do mouse.

Java oferece um amplo conjunto de classes pré-definidas e re-utilizáveis que simplificam o desen- volvimento de aplicações gráficas. A raiz desse conjunto de classes gráficas no pacote java.awt é a classe abstrata Component, que representa qualquer objeto que pode ser apresentado na tela e ter interação com usuários. Similarmente, o pacote Swing tem por raiz a classe JComponent, que (indiretamente) é em si uma extensão de Component.

Os métodos da classe Component definem funcionalidades que dizem respeito à manipulação de qualquer componente gráfico em Java. O método getSize() permite obter a dimensão do componente, expressa na forma de um objeto da classe Dimension — um objeto dessa classe tem

campos públicos width e height que indicam respectivamente as dimensões horizontais e verti- cais do componente em pixels. Já o método setSize() permite definir a dimensão do componente. Outro grupo de métodos importantes dessa classe é composto pelos métodos que permitem deter- minar os manipuladores de eventos que são relevantes para qualquer tipo de objeto gráfico,

 

como os eventos de mouse (addMouseListener(),

 

addMouseMotionListener(),

removeMouseListener(),

removeMouseMotionListener())

 

e de teclado

addKeyListener(),

removeKeyListener()).

 

Subclasses de Component que são amplamente utilizadas no desenvolvimento de aplicações gráficas incluem os containers, um objeto gráfico que pode conter outros componentes, e os compo- nentes de interface gráfica com usuários.

Para manipular texto em uma interface gráfica há componentes dos tipos campos de texto e áreas de texto, que permitem a entrada e/ou apresentação de texto em uma janela gráfica, respectivamente para uma única linha ou para diversas linhas de texto.

Quando a aplicação oferece ao usuário uma série de opções pré-estabelecidas, os componentes tipicamente utilizados em interfaces gráficas são de escolha, que pode ser única, quando a seleção de uma opção exclui as demais, ou múltipla, quando várias opções podem ser selecionadas simultanea- mente.

 

Rótulos

Rótulos são campos de texto incluídos em uma janela gráfica com o único objetivo de apresenta- ção. Dessa forma, rótulos não reagem a interações com usuários.

A classe Label define um componente de AWT que apresenta uma única linha de texto não- alterável. Um dos atributos associados a um Label é o texto a ser apresentado, que pode ser especi- ficado em um dos construtores ou através do método setText(). O método getText() retorna o valor desse atributo. Outro atributo de Label é o alinhamento, que pode ser Label.CENTER, Label.LEFT ou Label.RIGHT. O alinhamento pode ser definido em um dos construtores (jun- tamente com o texto) ou através do método setAlignment(). O método getAlignment() retorna o valor desse atributo.

A classe JLabel define o componente Swing para implementar rótulos. Além de oferecer fa- cilidades equivalentes àquelas de Label, JLabel oferece a possibilidade de definir o alinhamento vertical (o padrão é centralizado) e de usar um ícone gráfico como rótulo, em adição ou em substitui- ção a um texto.

 

Botões

Um botão corresponde a um componente gráfico que pode ser “pressionado” através de um “cli- que” do mouse, podendo assim responder à ativação por parte de um usuário.

A classe Button implementa um botão no AWT. Para criar um botão com algum rótulo, basta usar o construtor que recebe como argumento uma string com o texto do rótulo. Alternativamen- te, pode-se usar o construtor padrão (sem argumentos) e definir o rótulo do botão com o método setLabel(). O rótulo que foi definido para o botão pode ser obtido do método getLabel().

Em Swing, o componente que define um botão é JButton. Além de poder ser definido com

rótulos de texto, um botão Swing pode conter também ícones.

Para manipular o evento associado à seleção de um botão, é preciso definir uma classe que realize o tratamento de um evento do tipo ação implementando um ActionListener.

 

Campos de texto

Um campo de texto é uma área retangular, de tamanho correspondente a uma linha, que pode ser utilizada para apresentar ou adquirir strings do usuário.

A classe TextField é uma extensão da classe TextComponent que implementa um campo de texto no toolkit AWT. Os construtores dessa classe permitem a criação de um campo de texto que pode estar vazio ou conter algum texto inicial. O número de colunas no campo também pode ser especificado através dos construtores.

O componente correspondente a esse em Swing é JTextField. Além de funcionalidades associadas a TextField, esse componente oferece outras que permitem definir campos de texto customizados.

Quando o usuário entra um texto em um campo de texto e tecla ENTER, é gerado um evento do tipo ação. Para indicar o objeto manipulador para esse evento, os métodos addActionListe- ner() e removeActionListener() são utilizados. Ambos têm como argumento um objeto ActionListener. Eventos relativos à modificação de um valor em um campo de texto (sem pres- sionar a tecla ENTER) estão associados a eventos do tipo texto. Desse modo, é possível monitorar se alguma modificação ocorreu no campo de texto.

É possível não ecoar a apresentação dos caracteres de entrada para a tela definindo um caráter alternativo que deve ser apresentado. Em AWT, utiliza-se o método setEchoChar() da classe TextField. Em Swing, para tal fim deve ser utilizado o componente JPasswordField.

 

Áreas de texto

Uma área de texto permite apresentar e opcionalmente editar textos de múltiplas linhas. É pos- sível adicionar texto a uma área através do método append(), que recebe uma string como argu- mento.

Em AWT, a classe TextArea oferece essa funcionalidade, sendo também uma extensão da classe TextComponent. Assim como para um campo de texto de uma linha, o texto apresenta- do inicialmente e o número de colunas visíveis pode ser definido em construtores. Adicionalmente, o número de linhas visíveis pode ser definido no momento da construção do objeto. Para a apre- sentação de textos que ocupam uma área maior que a visível, um construtor tem um argumento  que indica se e quantas barras de rolagem estarão presentes na área de texto. Os valores possí-  veis, definidos como constantes estáticas dessa classe, são

 

SCROLLBARS_BOTH (barras de rolagem nas direções vertical e horizontal), SCROLLBARS_HORIZONTAL_ONLY (apenas na horizontal),

SCROLLBARS_VERTICAL_ONLY (apenas na vertical) e

SCROLLBARS_NONE (sem barras de ro- lagem).

O componente correspondente a esse em Swing é o JTextArea. Em Swing, o comportamento default para a área de texto é não ter barras de rolagem — se isso for necessário, a aplicação deve usar um componente decorador JScrollPane. Outras diferenças dizem respeito à habilidade de mudar de linha (automático em AWT) e à geração de eventos do tipo texto (não presente em Swing). O framework Swing oferece, no entanto, outros componentes mais elaborados que também ma- nipulam áreas de texto. Componentes das classes JEditorPane e JTextPane conseguem manipular vários tipos de conteúdo, como por exemplo texto em HTML.

 

Componentes de seleção

Java apresenta três mecanismos básicos para definir um componente que permite ao usuário a seleção de opções apresentadas: drop-down, lista ou caixa de seleção.

Um componente de interação em drop-down é aquele que deixa visível uma única opção (aquela que está selecionada) ao usuário, mas que permite que esse visualize e selecione as outras opções através da ativação de um mini-botão associado ao componente. Quando esse tipo de componente permite também a entrada de valores em um campo de texto combinado à lista, é usualmente chamado de combo box. Em componentes do tipo combo box, a seleção é restrita a uma única opção, com uma lista de itens em apresentação drop-down onde apenas o elemento selecionado é visível. Os demais itens podem ser apresentados para seleção através de interação com o mouse.

Em AWT, a funcionalidade de lista em drop-down é suportada através da classe Choice, que permite definir uma lista de itens não-editáveis. Para essa classe, apenas o construtor padrão é ofe- recido. Os principais métodos para manipular objetos dessa classe são: add(String s), que permite adicionar itens à lista; getSelectedIndex(), que retorna a posição numérica do item selecionado na lista (o primeiro elemento tem índice 0); getSelectedItem(), que retorna o ró- tulo do item selecionado na lista; e getItem(int index), retorna o rótulo do item cujo índice foi especificado.

A classe Choice implementa a interface ItemSelectable, o que permite associar objetos dessa classe a um manipulador de eventos do tipo item, ItemListener.

Em Swing, o mecanismo de lista em drop-down é suportado pela classe JComboBox, que pode ser configurado para ser editável ou não.

A segunda opção para seleção de itens é oferecida através de listas, onde vários itens são apre- sentados simultaneamente em uma janela. Na construção desse tipo de componente, o programador pode estabelecer se a seleção será exclusiva ou múltipla. Um componente do tipo lista oferece a fun- cionalidade de apresentar uma lista de opções dos quais um ou vários itens podem ser selecionados. Em AWT, a classe List oferece essa funcionalidade. Além do construtor padrão, essa classe oferece um construtor que permite especificar quantas linhas da lista serão visíveis. Um terceiro construtor permite especificar um valor booleano indicando se o modo de seleção de vários itens está habilitado (true) ou não. Se não especificado, apenas um item pode ser selecionado da lista.

Os principais métodos dessa classe incluem: add(String item), que acrescenta o item à lis- ta; add(String item, int index), que acrescenta um item à lista na posição especificada; getSelectedIndex(), que retorna a posição numérica do item selecionado na lista, sendo que o primeiro item está na posição 0; getSelectedItem(), que retorna o rótulo do item selecionado na lista; e getItem(int index), que retorna o rótulo do item cujo índice foi especificado. Há ainda métodos para a manipulação de seleções múltiplas: getSelectedIndexes(), que retorna um arranjo de inteiros correspondendo às posições selecionadas, e getSelectedItems(), que retorna um arranjo de String correspondente aos rótulos dos itens selecionados.

Assim como a classe Choice, a classe List implementa a interface ItemSelectable, que permite associar um objeto dessa classe a um manipulador de eventos do tipo item, ou seja, a um objeto ItemListener. Esse tipo de evento é gerado quando o item é alvo de um único toque do mouse. Além disso,  um item da lista pode reagir a um toque duplo do mouse através da geração  de um evento do tipo ação, usando para o tratamento do evento nesse caso um manipulador do tipo ActionListener. O tratamento de múltiplas seleções através de eventos de itens não é amigável através dessa interface, pois a cada seleção um novo  evento é gerado.  Por esse motivo,  a documentação da API Java recomenda que esse tipo de seleção múltipla em AWT seja efetivada através de algum controle externo, como um botão.

Em Swing, funcionalidade similar à apresentada por List é oferecida pela classe JList, em- bora algumas diferenças devam ser observadas. Por exemplo, ao contrário de List, um JList não suporta barras de rolagem diretamente, o que deve ser acrescido através de um objeto decorador JScrollPane.

A terceira opção para selecionar itens em uma interface gráfica é utilizar componentes do tipo caixas de seleção, que podem ser independentemente selecionadas ou organizadas em grupos de caixas de seleção das quais apenas uma pode estar selecionada. Uma caixa de seleção oferece um mecanismo de seleção em que uma opção pode assumir um de dois valores, selecionada ou não- selecionada. Diz-se que tais componentes têm comportamento booleano.

Em AWT, essa funcionalidade é suportada pela classe Checkbox. Além do construtor padrão, essa classe oferece construtores que permitem expressar um rótulo (String) para o botão e o es- tado inicial (boolean). O método getState() permite obter o estado da seleção; o método getLabel(), o rótulo associado à caixa de seleção. Checkbox implementa a interface Item- Selectable, permitindo tratar reações a eventos do tipo item através de um ItemListener.

Em Swing, essa funcionalidade é suportada de forma similar pela classe JCheckBox.

É também possível definir grupos de caixas de seleção das quais apenas uma pode estar seleci- onada em um dado instante, constituindo assim opções exclusivas entre si. Em AWT, esse tipo de funcionalidade é oferecido através do conceito de um grupo de checkboxes, suportado através da clas- se CheckboxGroup. Um CheckboxGroup não é um componente gráfico, como um container, mas sim um agrupamento lógico entre os componentes gráficos do tipo Checkbox, que devem es- pecificar na sua construção ou através do método setCheckboxGroup() a qual grupo pertencem, se for o caso de pertencer a algum.

Funcionalidade similar a essa é oferecida em Swing através das classes JRadioButton e But- tonGroup.

 

4.1.1 Containers

Dificilmente uma aplicação gráfica é composta por um único componente, mas sim por vários componentes inter-relacionados. Para este tipo de aplicação, um componente fundamental é a área onde os demais componentes da aplicação estarão dispostos. Um componente que pode conter outros componentes é denominado um container.

Em Java, a classe Container é a classe abstrata que define as funcionalidades básicas asso- ciadas a um container, tais como adicionar e remover componentes, o que é possível através dos métodos add() e remove(), respectivamente. É possível também estabelecer qual a estratégia de disposição de componentes no container, ou seja, qual o método de gerência de layout, através do método setLayout().

Window é uma classe derivada de Container cujos objetos estão associados a janelas. Cada objeto Window é uma janela independente em uma aplicação, embora a essa janela não estejam associadas as funcionalidades usualmente oferecidas por gerenciadores de janela. Raramente um objeto desse é usado diretamente, mas objetos dessa classe são muito utilizados através de suas subclasses, tais como Frame.

Outra classe derivada de Container de extensa aplicação é Panel, que define uma área de composição de componentes contida em alguma janela. A classe Applet é uma extensão de Panel que permite criar applets (Seção 4.3). Embora a classe JComponent, raiz da hierarquia para todos os componentes do novo framework para interfaces gráficas de Java, seja derivada da classe Container, não se pode acrescentar di- retamente um componente gráfico a qualquer componente Swing. Para as classes de Swing que correspondem a containers no sentido definido em AWT, ou seja, às quais podem ser acrescenta- dos outros componentes, deve-se obter uma referência ao objeto Container através do método getContentPane().

 

4.1.1 Janelas

Janelas são áreas retangulares que podem ser exibidas em qualquer parte da tela gráfica de um usuário. Em AWT, objetos da classe Window estão associados a essa funcionalidade, sendo que nessa classe são definidos os métodos aplicáveis a qualquer tipo de janela. O framework Swing oferece a classe JWindow, derivada da classe Window, com esse mesmo tipo de funcionalidade porém adaptada ao novo framework.

Um objeto Window, no entanto, corresponde a uma janela sem nenhum tipo de “decoração”. É mais comum que aplicações gráficas criem janelas através de uma das classes derivadas de Window, tais como um frame ou uma janela de diálogo. Convém destacar da classe Window os métodos show(), para trazer a janela para a frente da tela; dispose(), para eliminar uma janela e liberar os recursos usados por ela usados; e addWindowListener() e removeWindowListener() para associar ou remover objetos manipuladores de eventos de janelas.

Um frame agrega a uma janela as funcionalidades mínimas para que ela possa ser identificada e manipulada pelo usuário da aplicação, tais como uma borda e a barra de títulos com botões de controle da janela. Um frame é uma janela com propriedades adicionais que a habilitam a ter uma “vida independente” no ambiente de janelas gráficas. Para que um frame seja apresentado pela aplicação, um objeto desse tipo de classe deve ser criado e algumas de suas propriedades básicas — tais como sua dimensão e o fato de estar visível ou não — devem ser estabelecidas.

Em AWT, frames estão associados a objetos da classe Frame. Esse exemplo ilustra as operações essenciais para que um frame AWT seja criado e exibido em um ambiente gráfico. O resultado da execução desse código é a criação e exibição de uma janela gráfica com conteúdo vazio:

 

1    import java.awt.*;
2    import java.awt.event.*;
3    public class Janela extends Frame {
4    class WindowHandler extends WindowAdapter {
5    public void windowClosing(WindowEvent we) {
6        dispose();
7        System.exit(0);
8        }
9        public void windowActivated(WindowEvent we) {
10        we.getWindow().validate();
11        }
12    }    
13    public Janela() {
14    this("Janela");
15    }
16    public Janela(String titulo) {
17    setTitle(titulo);
18    setSize(320,200);
19    addWindowListener(new WindowHandler());
20    }
21    public static void main(String[] args) {
22    Janela j = new Janela();
23    j.setVisible(true);
24    }
25    }

 

Se o tamanho da janela (em pixels, horizontal e vertical) não for definido (método setSize()), então o default (x=0, y=0) é assumido, criando uma janela de dimensões nulas. Mesmo que o tama- nho seja definido, a janela quando criada é por padrão invisível — para exibi-la é preciso invocar o método setVisible().

Frame é uma extensão da classe Window que, por sua vez, é uma concretização de Contai-

ner, que é uma extensão de Component. Assim, é importante tomar contato com os diversos métodos definidos ao longo dessa hierarquia para ter uma noção mais precisa sobre o que é possível realizar com um objeto Frame. Por exemplo, para tratar eventos relativos a janelas é preciso asso- ciar ao frame um WindowListener através do método addWindowListener(), definido na classe Window.

A classe Frame propriamente dita define os métodos que dizem respeito ao que um frame tem de específico que uma janela (objeto da classe Window) não tem — por exemplo, setTitle() para definir um título para a barra da janela, setMenuBar() para incluir uma barra de menus e setCursor() para determinar o tipo de cursor a ser utilizado. O tipo de cursor é definido por uma das alternativas especificadas como constantes da classe Cursor, tais como HAND_CURSOR ou Cursor.TEXT_CURSOR.

O pacote Swing também oferece a sua versão de um frame através da classe JFrame, que é uma extensão da classe Frame. Esse código ilustra a criação de uma janela vazia, como acima, usando JFrame:​

1    import javax.swing.*;
2    import java.awt.event.*;
3    public class SJanela extends JFrame {
4    class WindowHandler extends WindowAdapter {
5    public void windowClosing(WindowEvent we) {
6    dispose();
7    System.exit(0);
8    }
9    }
10    public SJanela() {
11    this("Janela");

12    }
13    public SJanela(String title) {
14    setSize(200,120);
15    setTitle(title);
16    addWindowListener(new WindowHandler());
17    }
18    public static void main(String[] args) {
19    SJanela je = new SJanela();
20    je.show();
21    }
22    }

 

  1. Gerenciadores de layout

Quando um container tem mais de um componente, é preciso especificar como esses componen- tes devem ser dispostos na apresentação. Em Java, um objeto que implemente a interface Layout- Manager é responsável por esse gerenciamento de disposição. A interface LayoutManager2 é uma extensão de LayoutManager que incorpora o conceito de restrições de posicionamento de componentes em um container.

Os principais métodos da classe Container relacionados ao layout de componentes são se- tLayout(), que recebe como argumento o objeto que implementa LayoutManager e que deter- mina qual a política de gerência de disposição de componentes adotada; e validate(), usado para rearranjar os componentes em um container se o gerenciador de layout sofreu alguma modificação ou novos componentes foram adicionados.

 

O pacote java.awt apresenta vários gerenciadores de layout pré-definidos. As classes Flo- wLayout e GridLayout são implementações de LayoutManager; as classes BorderLayout, CardLayout e GridBagLayout são implementações de LayoutManager2. Swing acrescenta ainda um gerenciador de layout através da classe BoxLayout. Para determinar que o gerenciador de layout em um container c seja do tipo XXXLayout, é preciso invocar setLayout() passando como argumento um objeto gerenciador:

c.setLayout(new XXXLayout());

Containers derivados da classe Window têm como padrão um gerenciador de layout do tipo

BorderLayout, enquanto aqueles derivados de Panel usam como padrão FlowLayout.

 

FlowLayout

FlowLayout é uma classe gerenciadora de layout que arranja os componentes seqüencialmente na janela, da esquerda para a direita, do topo para baixo, à medida que os componentes são adiciona- dos ao container.

Os componentes são adicionados ao container da forma similar a um texto em um parágrafo, permitindo que cada componente mantenha seu tamanho natural. Como padrão, os componentes são horizontalmente centralizados no container. É possível mudar esse padrão de alinhamento especi- ficando um valor alternativo como parâmetro para um dos construtores da classe ou para o método setAlignment(). Esse parâmetro pode assumir um dos valores constantes definidos na classe, tais como LEFT ou RIGHT.

É possível também modificar a distância em pixels entre os componentes arranjados através desse tipo de gerenciador com os métodos setHgap() e setVgap(); alternativamente, esses valores podem também ser especificados através de construtores da classe FlowLayout. Para obter os valores utilizados, há métodos getHgap() e getVgap().

Esse é o gerenciador de layout padrão para containers derivados de Panel, tais como Applet.

Esse exemplo ilustra o uso desse tipo de gerenciador de layout para dispor um conjunto de botões em um frame:


1    import java.awt.*;
2    public class JanelaFlow extends Frame {
3    public JanelaFlow() {
4    setTitle("FlowLayout");
5    setSize(240,100);
6    setLayout(new FlowLayout());
7    }
8    public void addButton(int count) {
9    for(int i=1; i <= count; ++i)
10    add(new Button("B"+i));
11    }
12    public static void main(String[] args) {
13    JanelaFlow j = new JanelaFlow();
14    int qtde = 10;
15    try {
16    if (args.length > 0)
17    qtde = Integer.parseInt(args[0]);
18    }
19    catch (Exception e) {
20    }
21
22    j.addButton(qtde);
23    j.validate();
24    j.setVisible(true);
25    }
26    }

 

GridLayout

GridLayout é uma implementação de LayoutManager que permite distribuir componentes ao longo de linhas e colunas. A distribuição dá-se na ordem de adição do componente ao container, da esquerda para a direita e de cima para baixo. Essa classe oferece um construtor que permite especificar o número de linhas e colunas desejado para o grid — para o construtor padrão, um grid de uma única coluna é usado. Adicionalmente, há

outro construtor que permite especificar, além da quantidade de linhas e colunas, o espaço em pixels

entre esses componentes nas direções horizontal e vertical.

Deve-se observar que, nesse tipo de layout, as dimensões dos componentes são ajustadas para ocupar o espaço completo de uma posição no grid.

Esse exemplo mostra como GridLayout dispõe um conjunto de dez botões em um grid de três linhas e quatro colunas:

 

1    import java.awt.*;
2
3    public class JanelaGrid extends Frame {
4    private final int rows=3, cols=4;
5    public JanelaGrid() {
6    setTitle("GridLayout");
7    setSize(240,100);
8    setLayout(new GridLayout(rows, cols));
9    }
10
11    public void addButton(int count) {
12    int max = rows*cols;
13    if (count < max)
14    max = count;
15    for(int i=1; i <= max; ++i)
16    add(new Button("B"+i));
17    }
18
19    public static void main(String[] args) {
20    JanelaGrid j = new JanelaGrid();
21    int qtde = 10;
22    try {
23    if (args.length > 0)
24    qtde = Integer.parseInt(args[0]);
25    }
26    catch (Exception e) {
27    }
28
29    j.addButton(qtde);
30    j.validate();
31    j.setVisible(true);
32    }
33    }

 

BorderLayout

BorderLayout é uma implementação de LayoutManager2 adequado para janelas com até cinco componentes. Ele permite arranjar os componentes de um container em cinco regiões, cujo posicionamento é representado pelas constantes BorderLayout.NORTH, SOUTH, EAST, WEST e CENTER:

 

  1. import java.awt.*;

  2. public class JanelaBorder extends Frame {

  3. public JanelaBorder() {

  4. setTitle("BorderLayout");

5                              setSize(240,100);

  1. add(new Button("North"), BorderLayout.NORTH);

  2. add(new Button("South"), BorderLayout.SOUTH);

  3. add(new Button("East"), BorderLayout.EAST);

  4. add(new Button("West"), BorderLayout.WEST);

  5. add(new Button("Center"), BorderLayout.CENTER);

11                   }

  1. public static void main(String[] args) {

  2. JanelaBorder j = new JanelaBorder();

  3. j.validate();

  4. j.setVisible(true);

16                   }

 

Observe a utilização do método add(Component c, Object o) da classe Container, que incorpora a especificação das restrições de posicionamento.

Além do construtor padrão, outro construtor permite especificar o gap horizontal e vertical (em

pixels) entre os componentes.

Esse tipo de layout é o padrão para containers do tipo Frame. Para utilizar esse tipo de geren- ciador para janelas com mais de cinco componentes, basta definir que o componente inserido em um BorderLayout seja um Panel, que é um container que pode ter seu próprio gerenciador de layout.

 

1    import java.awt.*;
2    public class JanelaBorder extends Frame {
3    public JanelaBorder() {
4    setTitle("BorderLayout");
5    setSize(240,100);
6    add(new Button("North"), BorderLayout.NORTH);
7    add(new Button("South"), BorderLayout.SOUTH);
8    add(new Button("East"), BorderLayout.EAST);
9    add(new Button("West"), BorderLayout.WEST);
10    add(new Button("Center"), BorderLayout.CENTER);
11    }
12    public static void main(String[] args) {
13    JanelaBorder j = new JanelaBorder();
14    j.validate();
15    j.setVisible(true);
16    }


CardLayout

Um gerenciador do tipo CardLayout empilha os componentes de um container de tal forma que apenas o componente que está no topo permanece visível. Esse gerenciador implementa a interface LayoutManager2.

Os métodos do gerenciador estabelecem funcionalidades que permitem “navegar” entre os com- ponentes empilhados, determinando qual item deve estar visível em um dado momento. Os méto- dos de navegação first(), next(), previous() e last() permitem realizar uma varredura seqüencial pelos componentes de container especificado, cujo gerenciador de layout deve ser do tipo CardLayout.

O método show() permite selecionar um componente para exposição diretamente através de uma string, que é especificada como uma restrição quando da adição do componente ao container, como no seguinte fragmento:

Panel p = new Panel(); cm = new CardLayout();

p.setLayout(cm);

Button b = new Button("Teste"); p.add(b, b.getLabel());

que permitiria a exibição desse botão com a invocação cm.show(p, b.getLabel());

Tipicamente, os componentes manipulados por um gerenciador do tipo CardLayout são con- tainers, os quais por sua vez utilizam qualquer outro tipo de gerenciador de layout.

Esse exemplo ilustra o uso de CardLayout para organizar cinco rótulos distintos em um contai- ner alocado ao elemento central de um frame com BorderLayout. Os quatro elementos periféricos do BorderLayout são usados para navegar entre os rótulos do CardLayout central:


1    import java.awt.*;
2    import java.awt.event.*;
3    public class JanelaCard extends Frame {
4    Panel central = new Panel();
5    CardLayout cl = new CardLayout();
6    class FirstHandler implements ActionListener {
7    public void actionPerformed(ActionEvent ae) {
8    cl.first(central);
9    }
10    }
11    class LastHandler implements ActionListener {
12    public void actionPerformed(ActionEvent ae) {
13    cl.last(central);
14    }
15    }
16    class NextHandler implements ActionListener {
17    public void actionPerformed(ActionEvent ae) {
18    cl.next(central);
19    }
20    }
21    class PreviousHandler implements ActionListener {
22    public void actionPerformed(ActionEvent ae) {
23    cl.previous(central);
24    }
25    }
26    public JanelaCard() {
27    setTitle("CardLayout");
28    setSize(240,200);
29    Button next = new Button("Proximo");
30    next.addActionListener(new NextHandler());
31    Button previous = new Button("Anterior");
32    previous.addActionListener(new PreviousHandler());
33    Button first = new Button("Primeiro");
34    first.addActionListener(new FirstHandler());
35    Button last = new Button("Ultimo");
36    last.addActionListener(new LastHandler());
37    add(first, BorderLayout.NORTH);
38    add(last, BorderLayout.SOUTH);
39    add(previous, BorderLayout.WEST);
40    add(next, BorderLayout.EAST);
41    add(central, BorderLayout.CENTER);
42    central.setLayout(cl);
43    central.add(new Label("Primeiro painel"), "Primeiro");
44    central.add(new Label("Segundo painel"), "Segundo");
45    central.add(new Label("Terceiro painel"), "Terceiro");
46    central.add(new Label("Quarto painel"), "Quarto");
47    central.add(new Label("Quinto painel"), "Quinto");
48    }
49    public static void main(String[] args) {
50    JanelaCard jc = new JanelaCard();
51    jc.setVisible(true);
52    }
53    }

 

Nesse exemplo, o rótulo “Primeiro painel” é inicialmente exibido por estar no topo da pilha do objeto Panel com gerenciador do tipo CardLayout. Se o botão “Próximo” for pressionado, o rótulo “Segundo painel” será exibido.

GridBagLayout

GridBagLayout é o gerenciador de layout mais flexível dentre aqueles pré-definidos em ja- va.awt; é também, em decorrência dessa flexibilidade, o mais complexo. É uma implementação de LayoutManager2 que permite, como GridLayout, arranjar componentes ao longo de uma matriz de linhas e colunas.

 

No entanto, componentes podem ser acrescentados em qualquer ordem e podem também variar em tamanho, podendo ocupar mais de uma linha ou coluna. Uma vez que o desenho da interface tenha sido especificado, a chave para a utilização desse gerenciador é a criação de um objeto de restrição de posicionamento. Esse objeto é da classe Grid- BagConstraints. Objetos da classe GridBagConstraints determinam como um gerencia- dor do tipo GridBagLayout deve posicionar um dado componente em seu container.

Uma vez que esse objeto tenha sido criado e suas restrições especificadas, basta associar essas restrições ao componente usando o método setConstraints() e adicioná-lo ao container com o método add() com o segundo parâmetro de restrições. A especificação das restrições de posiciona- mento, tamanho e propriedades de um componente nesse tipo de gerenciador é determinada através da atribuição de valores a campos públicos do objeto da classe GridBagConstraints.

O posicionamento é especificado pelas variáveis gridx e gridy, respectivamente para indicar

a coluna e a linha onde o componente deve ser posicionado. Para gridx, o valor 0 indica a coluna mais à esquerda.  Do mesmo modo, para gridy o valor 0 indica a linha mais ao topo.  Além de

valores absolutos de posicionamento, essa classe define a constante RELATIVE para posicionamento relativo, após o último componente incluído, sendo esse o valor padrão para esses campos.

O número de células que o componente ocupa no grid é indicado pelas variáveis gridwidth e gridheight, relacionadas respectivamente ao número de colunas e ao número de linhas que será ocupado pelo componente. O valor REMAINDER para esses campos indica que o componente será o último dessa linha ou coluna, devendo ocupar a largura ou altura restante. O valor padrão desses campos é 1.

Outras variáveis de restrição definidas nessa classe incluem weightx, weighty, fill e an- chor. Os atributos weightx e weighty indicam o peso, ou a prioridade, que o componente terá para receber porções de espaço extra horizontalmente ou verticalmente, respectivamente, quando o container é redimensionado e espaço adicional torna-se disponível. O padrão é um componente não receber espaço extra (valor 0).

O atributo  fill é utilizado quando a área para a apresentação do componente  é maior que  o tamanho natural do componente. Indica como a apresentação do componente irá ocupar a área disponível, podendo assumir os valores definidos em constantes da classe: NONE, não modifica o tamanho do componente (o padrão); VERTICAL, ocupa o espaço vertical mas não altera a largura do componente; HORIZONTAL, ocupa o espaço disponível na horizontal mas não altera a altura do componente; e BOTH, ocupa os espaço disponível nas duas dimensões.

O atributo anchor é utilizado quando o tamanho do componente é menor que a área da célula à qual ele foi alocado para indicar a posição do componente na célula. O padrão é CENTER, mas outros valores possíveis são NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST.

Esse exemplo de código ilustra a criação de uma janela com três regiões, sendo que a primeira região contém uma lista, a segunda um grupo de três botões e a terceira uma área de texto:


1    import java.awt.*;
2    import java.awt.event.*;
3    public class JanelaGridBag extends Frame {
4    GridBagLayout gb = new GridBagLayout();
5    private final int noLinhas = 3;
6    public JanelaGridBag() {
7    setTitle("GridBagLayout");
8    setSize(320,200);
9    setLayout(gb);
10    List lEsq = new List(noLinhas, true);
11    lEsq.add("Um");
12    lEsq.add("Dois");
13    lEsq.add("Tres");
14    lEsq.add("Quatro");
15    lEsq.add("Cinco");
16    lEsq.add("Seis");
17    lEsq.add("Sete");
18    Button add = new Button(">>");
19    Button clear = new Button("Clear");
20    Button close = new Button("Close");
21    TextArea tDir = new TextArea("", noLinhas,
22    10, TextArea.SCROLLBARS_NONE);
23    GridBagConstraints gc = new GridBagConstraints();
24    gc.gridx = 0;
25    gc.gridy = 0;
26    gc.gridwidth = 1;
27    gc.gridheight = 3;
28    gc.fill = GridBagConstraints.VERTICAL;
29    add(lEsq, gc);
30    gc.gridx = 2;
31    add(tDir, gc);
32    Insets margens = new Insets(4, 3, 4, 3);
33    gc.gridx = 1;
34    gc.gridy = 0;
35    gc.gridwidth = 1;
36    gc.gridheight = 1;
37    gc.fill = GridBagConstraints.BOTH;
38    gc.ipadx = 4;
39    gc.ipady = 4;
40    gc.insets = margens;
41    add(add, gc);
42    gc.gridy = 1;
43    add(clear, gc);
44    gc.gridy = 2;
45    add(close, gc);
46    }
47    public static void main(String[] args) {
48    JanelaGridBag jgb = new JanelaGridBag();
49    jgb.setVisible(true);
50    }
51    }

 

BoxLayout

Swing oferece um gerenciador de layout simples, com alto grau de flexibilidade, que é o Box- Layout. Nesse tipo de layout, componentes podem ser dispostos em uma única linha ou em uma única coluna, porém arranjos de componentes bem complexos podem ser obtidos através da combi- nação desses mecanismos.

Em BoxLayout os componentes mantêm sua dimensão natural, como em FlowLayout. A direção na qual os componentes serão dispostos — se da esquerda para a direita ou se de cima  para baixo — pode ser especificada no construtor da classe, através respectivamente das constantes X_AXIS ou Y_AXIS.

Tipicamente, esse tipo de layout não é utilizado diretamente, mas sim através de um container

do tipo Box, que adota BoxLayout como padrão único de gerenciamento de layout.

Esse código ilustra a construção de uma interface similar àquela usando GridBagLayout, porém construída usando caixas aninhadas:

1    import javax.swing.*;
2    public class JanelaBox extends JFrame {
3    public JanelaBox() {
4        setTitle("BoxLayout");
5        setSize(240,120);
6        Box h = Box.createHorizontalBox();
7        Box v = Box.createVerticalBox();
8        String[] lista = {"Um", "Dois", "Tres", "Quatro",
9        "Cinco", "Seis", "Sete"};
10        JList jl = new JList(lista);
11        jl.setFixedCellWidth(70);
12        int mis = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
13        jl.setSelectionMode(mis);
14        JScrollPane lEsq = new JScrollPane(jl);
15        lEsq.setMinimumSize(new java.awt.Dimension(100,100));
16        JButton add = new JButton(">>");
17        JButton clear = new JButton("Clear");
18        JButton close = new JButton("Close");
19        JTextArea tDir = new JTextArea();
20        v.add(add);
21        v.add(clear);
22        v.add(close);
23        h.add(lEsq);
24        h.add(v);
25        h.add(tDir);
26        getContentPane().add(h);
27        }
28        public static void main(String[] args) {
29        JanelaBox jb = new JanelaBox();
30        jb.setVisible(true);
31        jb.validate();
32        }
33    }    

apres.gráfica
applets

4.3          Desenvolvimento de applets

Applets são programas projetados para ter uma execução independente dentro de alguma outra aplicação, eventualmente interagindo com esta — tipicamente, um browser (navegador) Web. Assim, applets executam no contexto de um outro programa, o qual interage com o applet e determina assim sua seqüência de execução. Funcionalidades associadas a applets Java são agregadas no pacote java.applet.

O processo de criação de um applet é similar ao processo de criação de uma aplicação — o pro- grama deve ser criado por um editor de programas e o arquivo de bytecodes (com extensão .class) deve ser gerado a partir da compilação do arquivo fonte.

Uma vez criado ou disponibilizado o bytecode, é preciso incluir uma referência a ele em alguma página Web. Essa página Web, uma vez carregada em algum navegador, irá reconhecer o elemento que faz a referência ao applet, transferir seu bytecode para a máquina local e dar início à sua execução.

 

  1. Criação de applet

Para criar um applet, é preciso desenvolver uma classe que estenda a classe Applet: import java.applet.*;

public class MeuApplet extends Applet {

...

}

A classe Applet é uma extensão de uma classe container do tipo Panel. Assim, o desenvol- vimento de um applet segue as estratégias de desenvolvimento de qualquer aplicação gráfica.

Um applet deve ser referenciado a partir de uma página Web. Tipicamente, para referenciar um applet cujo bytecode esteja em um arquivo MeuApplet.class a partir de uma página usando HTML na versão 3.2, o seguinte elemento deve ser incluído na página:

<applet code="MeuApplet.class"

width=200 height=150>

</applet>

Atributos opcionais para o elemento APPLET incluem, entre outros, CODEBASE, que indica o diretório (na forma de um URL) onde está localizado o código do applet, e NAME, uma string para identificar o applet.

Para navegadores compatíveis com HTML na versão 4.01 ou superiores ou ainda que usem XHTML, a forma de referenciar um applet na página seria através da tag OBJECT:

<object codetype="application/java"

classid="java:MeuApplet.class" width="200" height="150">

</object>

O navegador irá então criar um espaço gráfico de 200 pixels de largura por 150 pixels de altura na página apresentada, irá transferir o código do applet do mesmo local de onde a página se originou para a máquina local e dará início à execução do applet dentro deste espaço usando a máquina virtual Java associada ao navegador.

É possível também passar argumentos para o código do applet a partir da página HTML que o referencia. Para tanto, o tag PARAM é utilizado no interior do elemento APPLET, ocorrendo tantas vezes quantos forem os argumentos para o applet. Cada ocorrência de PARAM tem dois atributos, name e value, que permitirão a identificação do argumento no código do applet:

<applet ...>

<param name="background" value="white">

</applet>

4.3.2    Execução de applets


Ao contrário das aplicações Java, a execução de um applet em uma página não é iniciada pelo mé- todo main(). Um applet é executado como uma thread subordinada ao navegador (ou à aplicação appletviewer, presente no ambiente de desenvolvimento Java). O navegador será responsável por invocar os métodos da classe Applet que controlam a execução de um applet.


Quando o applet é carregado pela primeira vez pelo navegador, o método init() é invocado pelo navegador. Esse método pode ser considerado funcionalmente equivalente ao método construtor em aplicações, pois será apenas executado no momento em que o código for carregado.
Após o carregamento do applet para a máquina local e a execução do método init(), o método start() é invocado. Esse método será invocado também a cada vez que a área do applet torna-se visível no navegador, dando reínicio a operações que eventualmente tenham sido paralisadas pelo método stop().


O método stop() é invocado cada vez que o applet torna-se temporariamente invisível, ou seja, que sua área não esteja visível no navegador. É uma forma de evitar que operações que demandem muitos ciclos de CPU continuem a executar desnecessariamente. Também é invocado imediatamente antes de destroy(), que é invocado quando o applet está para ser eliminado do navegador. Este método serve para liberar recursos — além de memória — que o applet tenha eventualmente alocado para sua execução.
Esse exemplo ilustra a definição de um applet que implementa apenas esses quatro métodos. Quando carregada em um navegador Web, a página que referencia esse código apresenta apenas uma área vazia; porém, se o console Java do navegador for aberto, será possível visualizar as mensagens:


1    import java.applet.*;
2    public class MeuApplet extends Applet {
3    public void init() {
4    System.out.println("MeuApplet inicializado com dimensao "
5    + getSize());
6    }
7    public void start() {
8    System.out.println("MeuApplet deve executar");
9    }
10    public void stop() {
11    System.out.println("MeuApplet deve parar");
12    }
13    public void destroy() {
14    System.out.println("Adeus, MeuApplet");
15    }


Sendo Applet um componente gráfico do tipo container, todas as funcionalidades descritas para o desenvolvimento de aplicações gráficas aplicam-se a applets. Por exemplo, é possível que um applet apresente formas geométricas em seu contexto gráfico usando o método paint() ou incluir componentes de interface com usuário e tratar a manipulação de eventos.


Alguns métodos da classe Applet permitem a comunicação do applet com o navegador no qual ele está inserido. O método getParameter() permite a obtenção dos parâmetros passados pelo navegador para o applet. O método showStatus() exibe uma mensagem na linha de status do
 

navegador (na barra inferior da janela) que executa o applet. O método getAppletContext() permite obter o contexto de execução do applet, o que permite por exemplo estabelecer a comunica- ção entre dois applets de uma mesma página.Como o código de um applet é geralmente obtido através de uma conexão remota, sua execução está restrita aos princípios de segurança impostos por Java para a execução de código em ambientes distribuídos. Tipicamente, um applet não pode acessar arquivos e informações do ambiente onde está executando nem pode estabelecer conexões remotas com outras máquinas, a não ser aquela de onde o próprio código foi obtido.

4.3.3    Passagem de parâmetros


O método getParameter() da classe Applet permite obter parâmetros passados para o applet através do elemento PARAM em uma página HTML. O argumento para esse método é o nome do parâmetro, estabelecido no atributo name da tag PARAM, e o retorno é uma string com o valor do argumento — ou seja, o conteúdo do atributo value de PARAM. Esse applet recebe como parâmetro a cor de fundo, que pode ser branca, amarela ou cinza (o padrão):


1    import java.applet.*;
2    import java.awt.*;
3    public class AppletParameter extends Applet {
4    private int altura;
5    private int largura;
6    private int incremento;
7    public void init() {
8    Dimension d = getSize();
9    altura = d.height - 2;
10    largura = d.width;
11    incremento = altura/3;
12    Color c = Color.gray;
13    String cor = getParameter("background");
14    try {
15    if (cor.equalsIgnoreCase("yellow"))
16    c = Color.yellow;
17    else if (cor.equalsIgnoreCase("white"))
18    c = Color.white;
19    }
20    catch (Exception e) {
21    }
22
23    setBackground(c);
24    }
25    public void paint(Graphics g) {
26    int x=1;
27    int y=1;
28    int finalPos = largura - altura;
29    while (x < finalPos) {
30    g.setColor(new Color((float)Math.random(),
31    (float)Math.random(),
32    (float)Math.random()));
33    g.drawRect(x, y, altura, altura);
34    x += incremento;
35    }
36    }
37    }

 

Se na página HTML o elemento especificado for

 

<p>

<APPLET code="AppletParameter.class" width="640" height="20">

<PARAM name="background"  value="White">

</APPLET>

o applet será apresentado com fundo branco; com

 

<p>

<APPLET code="AppletParameter.class" width="640" height="20">

<PARAM name="background"  value="yellow">

</APPLET>

será apresentado com fundo amarelo. Se for invocado sem argumento, tendo na página HTML o elemento

<p>

<APPLET code="AppletParameter.class" width="640" height="20">

</APPLET>

os retângulos serão desenhados sobre um fundo cinza.

 

4.1.1 Contexto de execução

Em algumas situações pode ser de interesse fazer com que o applet interaja com o contexto no qual ele está executando (usualmente, o navegador Web). A interface AppletContext especifica algumas funcionalidades que permitem essa interação.

Para obter o contexto no qual o applet está executando, o método getAppletContext() da classe Applet é utilizado. Esse método retorna um objeto AppletContext, a partir do qual é possível obter referências a outros applets no mesmo contexto através de uma enumeração, usando o método getApplets(). Alternativamente, se ao applet foi atribuído um nome usando o atributo name na tag APPLET, uma referência a esse applet pode ser obtida através do método getAp- plet().

Como exemplo, considere a execução de dois applets em uma página, onde um applet tem um campo de texto para obter uma entrada do usuário e o outro tem uma área de texto para exibir as entradas que o usuário digitou na outra janela. O primeiro applet é definido em uma classe Entrada, enquanto que o segundo é definido em uma classe TextAreaApplet. A inclusão em uma página HTML dá-se através de dois elementos APPLET:


<p>
<applet code="Entrada.class" width="200" height="100" name="entra">
</applet>
<applet code="TextAreaApplet.class" width="200" height="100" name="mostra">
</applet>
</p>
O applet TextAreaApplet tem simplesmente uma área para exibição de texto:
1    import java.awt.*;
2    import java.applet.*;
3    public class TextAreaApplet extends Applet {
4    TextArea ta = new TextArea(5, 30);
5    public void init() {
6    add(ta);
7    }
8    public void append(String s) {
9    ta.append("\n" + s);
10    }
11    }


O applet Entrada define um campo para entrada de texto e estabelece a conexão entre os contextos em seu método de inicialização:

1    import java.applet.*;
2    import java.awt.*;
3    import java.awt.event.*;
4    public class Entrada extends Applet {
5    TextField tf = new TextField(30);
6    TextAreaApplet ta;
7    public void init() {
8    add(tf);
9    ta = (TextAreaApplet) getAppletContext().getApplet("mostra");
10    tf.addActionListener(new ActionListener() {
11    public void actionPerformed(ActionEvent ae) {
12    ta.append(tf.getText());
13    }
14    });

15    }
16    }

 

É possível também determinar que o navegador deve carregar um novo documento a partir de um applet, usando o método showDocument() de AppletContext. O argumento para esse método é um objeto localizador uniforme de recursos, do tipo java.net.URL.

Aplic.Distribuídas

Desenvolvimento de aplicações distribuídas

Entre os atrativos de Java está a facilidade que essa linguagem oferece para desenvolver aplicações para execução em sistemas distribuídos. Já em sua primeira versão, Java oferecia facilidades para o desenvolvimento de aplicações cliente-servidor usando os mecanismos da Internet, tais como os protocolos TCP/IP e UDP.


Se o cliente na aplicação distribuída precisa acessar um servidor de banco de dados relacional, Java oferece uma API específica para tal fim, JDBC. Através das classes e interfaces desse pacote é possível realizar consultas expressas em SQL a um servidor de banco de dados e manipular as tabelas obtidas como resultado dessas consultas.


Em termos de desenvolvimento voltado para a World-Wide Web, Java oferece o já clássico mecanismo de applets, código Java que executa em uma máquina virtual no lado do cliente Web, como descrito na Seção 4.3. O mecanismo de servlets permite associar o potencial de processamento da plataforma Java a servidores Web, permitindo construir assim uma camada middleware baseada no protocolo HTTP e em serviços implementados em Java.


Aplicações distribuídas mais elaboradas podem ser desenvolvidas usando uma arquitetura de objetos distribuídos, onde aplicações orientadas a objetos lidam diretamente com referências a objetos em processos remotos. Java oferece duas alternativas nessa direção,

RMI (Remote Method Invocation), uma solução 100% Java, e

Java IDL, uma solução integrada à arquitetura padrão CORBA.

Um passo adiante na evolução desse tipo de sistema é a utilização do conceito de agentes móveis, onde não apenas referências a objetos são manipuladas remotamente mas os próprios objetos — código e estado — movem-se pela rede.

 

5.1    Programação  cliente-servidor


O paradigma de programação distribuída através da separação das aplicações entre servidores (aplicações que disponibilizam algum serviço) e clientes (aplicações que usam esses serviços) foi  a arquitetura de distribuição predominante nos anos 1990. Um dos seus atrativos é o aumento da confiabilidade (a falha de uma máquina não necessariamente inviabiliza a operação do sistema como um todo) e redução de custo (máquinas mais simples podem executar os serviços isoladamente, ao invés de ter uma grande máquina fazendo todos os serviços).

As aplicações clientes e servidoras são programas executando em máquinas distintas, trocando informação através de uma rede de computadores. Para que os serviços possam ser solicitados, a aplicação cliente deve conhecer quem fornece o serviço (o endereço da aplicação servidora) e qual o protocolo pré-estabelecido para realizar a solicitação. Entre as vantagens citadas para o modelo de programação cliente-servidor destacam-se:

Relaciona a execução de processos distintos.


Oferece uma estruturação do processamento distribuído baseado no conceito de serviços, tendo um servidor, o provedor de serviços, e um cliente, o consumidor de serviços oferecidos.
Permite compartilhamento de recursos, com o servidor atendendo a vários clientes.
Oferece transparência de localização, com tratamento uniforme independentemente de processos estarem na mesma máquina ou em máquinas distintas.


Permite a comunicação através da troca de mensagens, oferecendo uma arquitetura fracamente acoplada através das mensagens para solicitações (cliente para servidor) e respostas (servidor para cliente).
Encapsula serviços, pois o cliente não precisa saber como servidor implementa o serviço, mas apenas a interface para solicitação e resposta.


Estaremos estudando aqui como Java oferece e simplifica o suporte a esse tipo de programação através das funcionalidades do pacote java.net. A partir da apresentação de alguns conceitos pre- liminares, apresenta-se os fundamentos Java para criar aplicações distribuídas usando os mecanismos de TCP/IP, UDP e HTTP.

 

5.1.1    Conceitos preliminares


A programação em redes de computadores é atualmente a regra, não a exceção. A principal vantagem nesse modelo de programação é a possibilidade de distribuir tarefas computacionalmente pesadas e conjuntos extensos de dados e informações entre diversas máquinas.


No entanto, há um custo associado a essa distribuição. Há necessidade de trocar mensagens entre as máquinas envolvidas no processamento, com um custo (tempo) adicional necessário para efetivar essa troca.
É importante também que os dispositivos envolvidos na troca de mensagens comuniquem-se usando uma mesma linguagem, ou protocolo. Protocolos são organizados em camadas (ou pilhas) de diferentes níveis de abstração. O conjunto de protocolos mais comuns na programação em rede é aquele estabelecido pela arquitetura TCP/IP, que opera com um software de suporte oferecido pelo sistema operacional de uma máquina ligada em rede.

 


 

A arquitetura TCP/IP organiza uma rede de computadores em quatro camadas:
 

Interface de rede: define padrões de conexão à rede física, seja local (Ethernet-CSMA/CD, Token Ring, FDDI, ATM) ou de longa distância (HDLC, X.25, ATM). Opera com endereços físicos, realizando a conversão entre endereços físicos e lógicos através dos protocolos ARP (Address Resolution Protocol) e RARP (Reverse ARP).

 

Inter-redes: protocolos para transporte não-confiável de mensagens (IP — Internet Protocol), para controle da comunicação e informe de erros (ICMP — Internet Control Message Protocol) e para roteamento de mensagens (EGP — Exterior Gateway Protocol, RIP — Routing Information Protocol). Endereços para comunicação host a host são lógicos (endereços IP).


Transporte: protocolos para transporte confiável de dados por conexão (TCP/IP — Transfer Control Protocol/IP) e para transporte de datagramas, sem conexão (UDP — User Datagram Protocol). Introduz o conceito de porta (endereço que identifica a aplicação na máquina). Endereços nesse nível são descritos por um par (host, port).
 

Aplicação: define conjunto de serviços manipulados por usuários. Serviços utilizam filosofia cliente-servidor, com os servidores estabelecendo portas para disponibilização do serviço. Algumas portas de serviços TCP/IP já são pré-definidas, sendo denominadas de portas notáveis.

Exemplos de portas notáveis incluem:

7, echo (reenvia o que recebe);

21, ftp (transferência de arquivos);

23, telnet (terminal virtual);

25, smtp (correio eletrônico); e

37, time (envia hora e data local da máquina).
 

Na versão corrente do protocolo, endereços IP ocupam 32 bits e são divididos em cinco classes. As classes A, B e C têm seus endereços estruturados em um prefixo de identificação de classe (binários 0, 10 e 110, respectivamente), identificador de subrede (7, 14 e 21 bits) e identificador de host. A classe D (prefixo 1110) é utilizada para multicast, enquanto que endereços da classe E (11110) são reservados para uso futuro.

Usualmente, endereços IP são representados por uma quádrupla de valores decimais correspondente aos quatro grupos de 8 bits do endereço. Nessa forma de representação, endereços iniciados por valores entre 0 e 127 são da classe A; entre 128 e 191, classe B; entre 192 e 223, classe C; entre 224 e 239, classe D; e entre 240 e 247, classe E.


Existe também uma forma de representação simbólica de endereços IP baseada em nomes de domínios. Domínios são partições da rede Internet organizados hierarquicamente em estruturas de domínios e sub-domínios. Existe um mapeamento entre endereços IP representados simbolicamente e numericamente, o qual é realizado por servidores de nome distribuídos pela Internet através do Sistemas de Nomes de Domínio (DNS).
 

O desenvolvimento de software na arquitetura TCP/IP segue a filosofia de particionamento em múltiplos processos concorrentes. Isso permite a simplificação de projeto, implementação e manipulação de software para ambientes distribuídos, assim como a gerência independente de protocolos em diversos níveis.
O software é organizado na forma de processos independentes. No nível mais próximo da máquina, isso permite o isolamento dos dispositivos físicos através da utilização de device drivers.


No nível da camada de transporte, dois tipos de processos são suportados.

A informação em processos TCP (entrada ou saída) é manipulada através do conceito de streams (arquivos seqüenciais).

Já a informação em processos UDP é manipulada através do conteúdo de pacotes datagramas, enviados sem verificação de conteúdo ou garantia de recebimento.

O processo IP atua como chaveador de datagramas.


As portas estabelecem a ligação entre o processo da aplicação e os processos IP, estando associadas a buffers finitos com acesso controlado. O acesso a portas pode sofrer bloqueio devido a uma tentativa de ler de uma porta com buffer vazio ou de escrever para porta com buffer cheio.

5.1.2    Aplicações TCP/IP
 

Para estabelecer a conexão TCP, é preciso identificar as extremidades dessa conexão tanto no processo cliente como no processo servidor. Essas extremidades são soquetes, identificados por um endereço de rede e um número de porta. A conexão pode ser interpretada como uma ligação direta entre os dois processos, através da qual os bytes podem fluir nos dois sentidos.


Um soquete TCP estabelece uma conexão stream bidirecional entre os endereços (hostC,portC) e (hostS,portS), ou seja, entre uma aplicação cliente em execução na máquina hostC controlando a porta portC e outra aplicação servidora em execução na máquina hostS monitorando a porta portS de hostS. A aplicação cliente utiliza a porta portC da máquina hostC para enviar solicitações de serviços e para receber retornos a suas solicitações. A aplicação servidora monitora constantemente a porta portS da máquina hostS aguardando a chegada de solicitações de serviço. Quando alguma solicitação é recebida, a aplicação servidora executa o serviço e utiliza a conexão para enviar o retorno com os resultados do serviço.


Java suporta a troca de bytes entre um cliente e um servidor TCP através do estabelecimento  de uma conexão entre eles. Todas as funcionalidades de Java referentes ao estabelecimento de uma conexão TCP estão agregadas no pacote java.net.

 

 

Clientes TCP em Java
 

Em Java, a classe que permite o estabelecimento de uma conexão pelo lado do cliente é Socket.
Para criar um soquete, o construtor da classe tipicamente utilizado é:

     public Socket(InetAddress address, int port) throws IOException


Uma vez que a conexão entre cliente e servidor tenha sido estabelecida pela criação dos correspondentes soquetes, os dados da aplicação podem fluir através dos streams a ela associados.
Os argumentos do construtor estabelecem o endereço IP da máquina remota na conexão. A classe InetAddress de java.net permite representar endereços IP como objetos Java. Uma vez que um objeto Java esteja representando um endereço IP, ele pode ser utilizado como parâmetro para métodos de outras classes que manipulam transferências de dados através dos protocolos TCP/IP.


Os métodos estáticos dessa classe permitem definir tais objetos associados à representação simbólica ou numérica de endereços IP — InetAddress.getByName(String host) — ou associados à máquina local — InetAddress.getLocalHost().


Este exemplo ilustra a manipulação de endereços IP através dos métodos dessa classe:

 

1    import java.net.*;
2    public class WhoIs {
3    public static void main(String[] args) {
4    try {
5    InetAddress myself = InetAddress.getLocalHost();
6    System.out.println("Local host is " +
7    myself.getHostName() +
8    " at IP address " +
9    myself.getHostAddress());
10    }
11    catch (UnknownHostException uhe) {
12    System.err.println(uhe);
13    }
14    // Processamento dos argumentos na linha de comando
15    int count = 0;
16    InetAddress otherHost;
17    while (count > args.length) {
18    try{
19    otherHost = InetAddress.getByName(args[count]);
20    System.out.println("Host " + otherHost.getHostName() +
21    " is at IP address " +
22    otherHost.getHostAddress());
23    }
24    catch (UnknownHostException uhe) {
25    System.err.println(uhe);
26    }
27    ++count;
28    }
29    }
30    }


A classe Socket oferece também métodos para obter informações sobre os endereços (máquina e porta) envolvidos na conexão e para estabelecer timeouts associados à conexão.
Quando um soquete é criado, automaticamente são estabelecidos streams de entrada e de saída para a transferência de dados pela conexão. Os métodos getInputStream() e getOutputS- tream() da classe Socket permitem identificar os objetos associados a esses streams.


Streams implementam o conceito de cadeias unidirecionais de dados (FIFO, First-In, First-Out) apenas de escrita ou apenas de leitura. Assim, uma aplicação pode obter dados do início de um stream de entrada e pode enviar dados para o final de um stream de saída de dados, sempre seqüencialmente. Streams em Java são suportados por classes do pacote java.io (Seção 3.2). Para leitura seqüen- cial de bytes utiliza-se um objeto da classe InputStream; para enviar bytes para um stream utiliza- se um objeto OutputStream e seus métodos write(), para agregar bytes ao stream, e flush(), para assegurar que os bytes inseridos sejam efetivamente encaminhados a seu destino.

 

Servidores TCP em Java
 

O processo servidor na arquitetura TCP deve estar preparado para responder a solicitações de conexões por parte dos clientes, permanecendo em estado de espera entre solicitações. Assim, um servidor TCP/IP deve realizar duas tarefas básicas: permanecer em execução aguardando (listening) a chegada de requisições em alguma porta pré-especificada; e responder à solicitação através de uma conexão estabelecida com o cliente em função da requisição recebida.


Em Java, a classe que permite a criação de servidores com essa funcionalidade é ServerSocket, também do pacote java.net. O principal método desta classe é accept(), que implementa a espera bloqueada por uma solicitação no endereço de porta especificado na construção do objeto. O retorno desse método é um objeto da classe Socket que estabelece a conexão com a aplicação cliente. Esse exemplo mostra o código para um servidor que responde a qualquer solicitação com uma mensagem fixa, cujo conteúdo é a seqüência de bytes que compõe um endereço URL:

1    import    java.io.*;
2    import    java.net.*;
3    import    java.util.*;
4    public    class TCPServer1 {
5    public static void main(String[] args) {
6    ServerSocket ss = null;
7    Socket cliente = null;
8    OutputStream os = null;
9    try {
10    ss = new ServerSocket(0);
11    System.out.println("Server: Aguardando na porta " +
12    ss.getLocalPort());
13    while (true) {
14    cliente = ss.accept();
15    os = cliente.getOutputStream();
16    System.out.println("Server: " +
17    "Processando solicitacao de " +
18    cliente.getInetAddress().getHostName());
19    String data =
20    "http://www.dca.fee.unicamp.br/cursos/PooJava/";
21    byte[] buffer = data.getBytes();
22    System.out.println("Server: Enviando \"" +
23    new String(buffer) + "\"");
24    os.write(buffer);
25    os.flush();
26    }
27    }
28    catch (Exception e) {
29    System.err.println(e);
30    }
31    finally {
32    try {
33    os.close();
34    cliente.close();
35    ss.close();
36    }
37    catch (Exception e) {
38    e.printStackTrace();
39    }
40    }
41    }
42    }

Uso de múltiplas threads
 

Tipicamente, por razões de eficiência, um servidor TCP é implementado como um processo multithreaded. Um dos possíveis problemas na execução de aplicações segundo o modelo cliente-servidor está associado com o tempo de atendimento a uma solicitação pelo servidor. Se o servidor for um processo monolítico, ele estará indisponível para receber novas requisições enquanto a solicitação não for completamente atendida. A solução para este problema depende da possibilidade de se estabelecer um processamento independente para o atendimento de cada solicitação ao servidor, liberando tão cedo quanto possível o servidor para receber novas solicitações.


O conceito de processamento independente é parte integrante da linguagem Java, através de multithreading. Todo processamento em Java está associado a alguma thread, sendo que novas threads de execução podem ser criadas a partir de qualquer thread. A criação de novas threads é em geral associada a classes que implementam a interface Runnable do pacote java.lang. Essa interface especifica o método run(), os quais são utlizados para criar objetos da classe Thread, com métodos start() e stop(), entre outros.


Com as facilidades suportadas pela linguagem, torna-se atrativo implementar servidores multithreaded, cujo corpo principal de processamento resume-se a um laço eterno para aceitar solicitações na porta especificada e criar um objeto thread para atender à solicitação recebida. A funcionalidade do serviço que será executado pela thread é definida no corpo do método run() que implementa a interface Runnable associada à thread criada. Cada thread criada existe exclusivamente durante o tempo necessário para atender à solicitação.
O seguinte exemplo revisita o servidor TCP anteriormente apresentado usando o mecanismo de múltiplas threads de execução:


1    import java.io.*;
2    import java.net.*;
3    import java.util.*;
4    class DataProvider implements Runnable {
5    Socket client;
6    OutputStream os = null;
7    public DataProvider(Socket s) throws IOException {
8    client = s;
9    os = client.getOutputStream();
10    }
11    public void run() {
12    String data =
13    "http://www.dca.fee.unicamp.br/courses/PooJava/";
14    byte[] buffer = data.getBytes();
15    try {
16    os.write(buffer);
17    os.flush();
18    os.close();
19    client.close();
20    }
21    catch (Exception e) {
22    System.err.println(e);
23    }
24    }
25    }
26    public class TCPServer2 {
27    public static void main(String[] args) {
28    ServerSocket ss = null;
29    Socket cliente = null;
30    try {
31    ss = new ServerSocket(0);
32    System.out.println("Server: Aguardando na porta " +
33    ss.getLocalPort());
34    while (true) {
35    cliente = ss.accept();
36    System.out.println("Server: " +
37    "Processando solicitacao de " +
38    cliente.getInetAddress().getHostName());
39    DataProvider dp = new DataProvider(cliente);
40    new Thread(dp).start();
41    }
42    }
43    catch (Exception e) {
44    System.err.println(e);
45    }
46    finally {
47    try {
48    ss.close();
49    }
50    catch (Exception e) {
51    System.err.println(e);
52    }
53    }
54    }
55    }

 

5.1.3    Aplicações UDP


Aqui serão apresentadas as funcionalidades que Java oferece para a programação cliente-servidor usando o protocolo de transporte UDP. A principal diferença em relação à programação cliente-servidor em TCP é que o protocolo UDP não suporta o conceito da transferência por streams de dados. UDP trabalha diretamente com o conceito de pacotes (datagramas). Assim, UDP não oferece a garantia de envio ou recepção e nem de
ordenação correta dos pacotes. Por outro lado, a ausência desses mecanismos permite uma transferência mais rápida. O endereçamento em UDP dá-se como para a programação TCP, usando a classe Java InetAddress.

Soquetes UDP
 

Assim como para o protocolo TCP, UDP estabelece uma conexão entre o processo da aplicação e a rede através de um soquete. Em Java, soquetes UDP são manipulados através de objetos da classe DatagramSocket. O construtor padrão para essa classe cria um soquete local na primeira porta disponível. Alternativamente, outro construtor permite especificar o número da porta desejado.
Ao contrário da classe Socket, um DatagramSocket não estabelece uma conexão com uma máquina remota, mas simplesmente um acesso local para a rede que pode ser utilizada para enviar e receber pacotes, através dos métodos send() e receive(), respectivamente.


Há um método connect() associado a objetos da classe DatagramSocket; no entanto, esse método atua como um filtro, só permitindo enviar ou receber pacotes para ou de um endereço IP ao qual o soquete foi conectado. O método disconnect() permite desconectar um soquete de um destino pré-especificado.

Datagramas
 

Os pacotes enviados e recebidos através dos soquetes UDP são objetos Java da classe DatagramPacket. Objetos dessa classe podem ser construídos de duas maneiras, dependendo se serão enviados ou recebidos através do soquete UDP: Pacotes a enviar. Nesse caso, deve ser utilizado o construtor que incorpora em seus argumentos o arranjo de bytes a enviar, seu tamanho, e o endereço de destino (máquina, especificada pelo seu endereço IP, e porta).


Pacotes a receber.

 

Nesse caso, os argumentos especificam apenas o arranjo de bytes para onde o conteúdo do pacote será transferido e o limite no tamanho do pacote que será recebido nesse arranjo.
Uma vez que um pacote tenha sido recebido, a informação sobre sua origem pode ser obtida através dos métodos getAddress() e getPort(). Os dados efetivamente recebidos podem ser extraídos do pacote usando o método getData(); o método getLength() permite determinar a dimensão dos dados.

Multicast
 

Um MulticastSocket é uma especialização de um DatagramSocket que permite que uma aplicação receba pacotes datagramas associados a um endereço multicast (classe D, endereços entre 224.0.0.1 e 239.255.255.255). Não é preciso nenhuma funcionalidade especial para apenas enviar datagramas para um endereço multicast. Todos os soquetes multicast que estejam inscritos em um endereço multicast recebem o datagrama que foi enviado para esse endereço e porta. Para gerenciar a inscrição de um soquete em um endereço multicast, dois métodos são oferecidos na classe MulticastSocket.


O primeiro, joinGroup(InetAddress m), permite à aplicação juntar-se a um grupo multicast. É o método que inscreve o soquete no grupo associado ao endereço multicast especificado como argumento. O outro método, para desligar-se de um grupo multicast, é leaveGroup(InetAddress m), que desconecta o soquete do grupo multicast especificado.

5.1.4    Aplicações HTTP
 

A World Wide Web (WWW ou simplesmente Web) é a primeira concretização de uma rede mundial de informação através de computadores. Proposta em 1989 no CERN (Suíça) por Tim-Berners Lee, ela interconecta principalmente documentos hipertexto (expressos em HTML — HyperText Markup Language) usando a infra-estrutura da Internet para a transferência de informação. Sua difusão expandiu-se principalmente após a popularização de interfaces gráficas com o usuário para navegação em hipertexto, iniciada a partir do lançamento do aplicativo Mosaic no NCSA (EUA).


A Web pode ser vista como um serviço de aplicação da arquitetura TCP/IP. Como tal, a arquitetura da Web define um esquema de endereçamento (URL) no nível da aplicação; estabelece protocolos de sessão (soquetes TCP/IP) e apresentação (HTML e auxiliares); define um protocolo (HTTP) no nível da aplicação; e segue o modelo cliente-servidor. As aplicações clientes na Web são usualmente navegadores (browsers) responsáveis pela apresentação de documentos HTML e pela solicitação de um recurso a servidores Web. O recurso pode ser um documento HTML ou um arquivo contendo informação texto ou binária (imagem, áudio, vídeo, applet Java, etc), conforme estabelecido pelo elemento que originou a solicitação.


Não necessariamente o software navegador precisa saber manipular todos os tipos de recursos, podendo ele ativar rotinas auxiliares de exibição para tipos não reconhecidos. As rotinas auxiliares podem ser ativadas sob a forma de programas executáveis externos ao navegador ou através de plugins. O conceito de plug-in foi desenvolvido pela Nestcape, sendo constituído por uma interface de programação (API) padronizada para ativar funcionalidades carregadas dinamicamente.


Servidores Web são responsáveis por atender a solicitações de clientes, por default operando  na porta notável 80. As funcionalidades básicas de servidores Web incluem: organizar e gerenciar os recursos HTTP; prover acesso seguro a esses recursos; e processar scripts (extensões CGI, por exemplo — ver Seção 5.3).
Java também oferece suporte ao desenvolvimento de aplicações sobre a Web usando recursos do pacote java.net. Dentre os conceitos oferecidos pela linguagem Java para a programação distribuída há uma grande ênfase na programação direcionada para a Web, com suporte para a manipulação de recursos Web através de URL, a manipulação de conexões HTTP, a conversão de strings para o formato de codificação www-urlencoded e suporte à manipulação de conteúdos de diferentes tipos MIME.

 

Endereçamento por URL


Um endereço localizador uniforme de recursos (URL) é definido por uma seqüência de caracteres que identifica de forma global e precisa um recurso na Web. A forma genérica de um URL é

esquema:parte_específica onde o esquema identifica o protocolo utilizado para manipular o recurso, tais como http, ftp, mailto, telnet e nntp. A parte específica descreve o endereço do recurso de acordo com a infra-estrutura e o tipo de recurso. Na Internet, a parte específica de um URL toma a forma genérica

     //user:password@host:port/url-path

onde nem todos os campos devem necessariamente estar presentes para todos os recursos endereçados.

 

Quando port é omitido, a porta notável para o esquema especificado é utilizada:

21 para ftp,

23 para telnet,

25 para mailto,

80 para http

O campo url-path é dependente do esquema.


A classe java.net.URL oferece a funcionalidade de nível mais alto (menor detalhamento) para a especificação de recursos Web. Cada recurso está associado a um objeto dessa classe, sendo que o localizador (URL) do recurso é especificado na construção do objeto. Uma vez que o objeto URL esteja instanciado, há três maneiras de realizar a transferência do conteúdo do recurso para a aplicação local.


Na primeira forma, através do método openStream(), obtém-se um fluxo de leitura de bytes que permite transferir o conteúdo do recurso. Outra possibilidade é usar o método openConnection(), que retorna um objeto da classe (abstrata) URLConnection. Esta classe permite manipular um maior número de detalhes referentes à conexão URL, tais como obter dimensão, tipo e codificação do conteúdo, manipulação do conteúdo associado a um stream de entrada, obtenção do cabeçalho e outras. Finalmente, é possível usar o método getContent(), que obtém o conteúdo do recurso diretamente. Nesse caso, um objeto ContentHandler específico para o tipo de recurso recebido será ativado.

Conexões HTTP
 

O protocolo no nível da aplicação para a transferência de hipertexto (HTTP, HyperText Transfer Protocol) opera sobre o protocolo TCP/IP para estabelecer um mecanismo de serviço com estrutura requisição-resposta. Uma das características peculiares de HTTP é a composição flexível do cabeçalho, composto por diversas linhas, o que permite sua utilização como integrador de diversos formatos e não apenas de documentos HTML.
Essa flexibilidade reflete-se também na maior complexidade desse protocolo. No entanto, é possível estabelecer servidores HTTP operando com configurações simplificadas, onde nem todos os serviços previstos no protocolo são implementados.


Os principais serviços de HTTP incluem:
 

GET: solicita ao servidor o envio de um recurso; é o serviço essencial para o protocolo.
HEAD: variante de GET que solicita ao servidor o envio apenas de informações sobre o recurso.
PUT: permite que o cliente autorizado armazene ou altere o conteúdo de um recurso mantido pelo servidor.
POST: permite que o cliente envie mensagens e conteúdo de formulários para servidores que irão manipular a informação de maneira adequada.

DELETE: permite que o cliente autorizado remova um recurso mantido pelo servidor.
 

Um cabeçalho HTTP é composto por uma linha contendo a especificação do serviço e recurso associado, seguida por linhas contendo parâmetros. Um exemplo de requisição gerada por um cliente HTTP é:
     GET http://www.dca.fee.unicamp.br/ Accept: text/html, image/gif, image/jpeg User-Agent: Mozilla/3.0
para a qual o cabeçalho da resposta poderia ser:
     HTTP/1.1 200 OK
     Date: Wed, 24 Mar 1999 23:23:45 GMT
     Server: Apache/1.2b6 Connection: close Content-Type: text/html Content-length: 648


A indicação do tipo de conteúdo do recurso (usada nos parâmetros Accept e Content-Type) seguem a especificação no padrão MIME (Multipurpose Internet Mail Extensions). A classe HttpURLConnection é uma especialização da classe URLConnection. Quando um objeto da classe URL invoca openConnection() é esse o tipo de conexão retornada quando o protocolo é HTTP. Além das funcionalidades de conexões URL, essa classe define várias constantes associadas especificamente ao protocolo HTTP (tais como os códigos de

erros) e alguns poucos métodos específicos de conexão HTTP.

 

Codificação e decodificação de dados


A tradução de strings para o formato esperado por um servidor Web a partir de um formulário, x-www-form-urlencoded, é suportada através da classe URLEncoder. Essa classe oferece o método estático encode(String s), retornando uma string com o conteúdo codificado do argumento.
O formato www-urlencoded agrega em uma única string uma série de pares na forma atributo=valor separados pelo símbolo ’&’.  Nesse formato, espaços são convertidos para o símbolo ’+’  e caracteres com conotação especial — tais como +, = e & — são representados por seqüência de escape ’%xx’ para a representação hexadecimal do valor ASCII do caráter. Assim, o caráter ’=’ que faça parte do nome de um atributo ou parte do conteúdo de um valor será codificado na string na forma ’%3d’.


O processo de tradução a partir de uma string nesse formato, que seria o necessário para implementar um serviço em Java que recebesse dados de um formulário, é oferecido pela classe URLDecoder, através do método estático decode(String).

tcp/ip
clie java
serv java
aplic udp
aplic http

(c) FMK 2023 - 2024.  Updated Sep 2024.

bottom of page