Avançar para a área de conteúdo

ir para o conteúdo principal

developerWorks Brasil  >  Tecnologia Java | Software livre  >

Dominando Grails: Entendendo Plug-ins

Combinar nova funcionalidade com facilidade

developerWorks
Opções de documento

Opções de documento que necessitam de JavaScript não são exibidas


Classificar esta página

Ajude-nos a melhorar este conteúdo


Nível: Introdutório

Scott Davis, Senior IT Architect, SDI Corp.

21/Jul/2009

Nesta parte de Dominando Grails , Scott Davis introduz o mundo de plug-ins do Grails. Incluir áreas inteiras de nova funcionalidade em seus aplicativos não poderia ser mais fácil. Você aprenderá como plug-ins fazem a sua mágica e usará um plug-in para implementar recursos de procura poderosos no aplicativo Blogito.
Sobre esta Série

Grails é uma estrutura de desenvolvimento da Web moderna que combina tecnologias Java familiares, como Spring e Hibernate com práticas contemporâneas, como convenção sobre configuração. Gravado em Groovy, Grails fornece integração com seu código Java de legado ao incluir a flexibilidade e o dinamismo de uma linguagem de script. Após aprender Grails, você nunca mais verá desenvolvimento na Web da mesma forma.

Desde de sua concepção, Dominando Grails foca as principais funcionalidades de Grails. Quanto mais você entender como as partes básicas se encaixam, mais fácil é combiná-las e construir um aplicativo acabado sofisticado. E, apesar de ter mencionado plug-ins rapidamente aqui e ali, evitei conscientemente entrar em detalhes sobre eles. Ou seja, até agora.

Nos próximos diversos artigos, explorarei o ecossistema de plug-ins do Grails. Desde de sua concepção, a plataforma Grails foi construída com capacidade de plug-ins em mente. Com essa pequena, porém, importante ênfase, é possível tirar proveito facilmente dos literalmente centenas de bits de funcionalidade pré-empacotados.

Quando este artigo foi escrito, o script Groovy da Lista 1 retornou 225 plug-ins. (Para um insight sobre como esse script funciona, consulte ""Praticamente Groovy: Construindo, Analisando e Efetuando Slurp de XML.")


Lista 1. Script Groovy Simples que Conta os Plug-ins de Grails Disponíveis

def addr = "http://plugins.grails.org/.plugin-meta/plugins-list.xml"
def plugins = new XmlSlurper().parse(addr)
def count = 0
plugins.plugin.each{
  println it.@name
  count++
}
println "Total number of plugins: ${count}"

Para obter uma lista mais fácil e simples para o ser humano, pode-se digitar grails list-plugins no prompt de comandos ou visitar o site Plug-ins do Grails (consulte Recursos).

O que É um Plug-in?

Desenvolvedores avançados de Java™ são caçadores e colecionadores experientes. Não sonhariam em gravar sua própria biblioteca de criação de log; em vez disso, eles simplesmente soltam o JAR log4j em seu caminho de classe. Precisa de um analisador de XML? Inclua o JAR Xerces em seu projeto. Esses bits de funcionalidade de plug-in demonstram a capacidade de reutilização da programação orientada a objetos.

Os plug-ins do Grails têm o mesmo propósito, mas em escala maior. Podem incluir muitos JARs, assim como GroovyServer Pages (GSPs), controladores, TagLibs, serviços e mais. De forma semelhante a como SiteMesh combina dois GSPs em um, plug-ins, efetivamente, combinam dois ou mais aplicativos Grails em um. É possível focar seus requisitos de negócios principais e combinar a funcionalidade adicional necessária — procura, autenticação, camadas de apresentação alternativas e mais — de recursos externos.

E plug-ins são realmente recursos externos . A equipe de desenvolvimento do Grails gravou diversos plug-ins valiosos, mas a grande maioria vem da comunidade. Na verdade, a equipe Grails continua a produzir funcionalidade principal em plug-ins conforme apropriado, tornando o próprio Grails menor e mais estável em cada release.

Como isso se aplica ao Blogito — o aplicativo de "pequeno blog" que está sendo construído nesta série? Suponha que o próximo bit de funcionalidade que deseja incluir seja o recurso de procura local. Se quiser combinar uma solução, em vez de construir sua própria infraestrutura de procura do início, leia adiante.



Voltar para parte superior


Instalando o Plug-in Searchable

O plug-in Searchable fornece recursos de procura semelhantes ao Google para seu aplicativo. Usa o Apache Lucene para criar os índices e o Compass para enganchar a indexação no ciclo de vida do Grails Object Relational Mapping (GORM)/Hibernate (consulte Recursos). Isso significa que toda vez que você criar, atualizar ou excluir uma classe de domínio, o índice do Lucene é atualizado conforme necessário.

Para instalar o plug-in, digite grails install-plugin searchable. (A próxima seção entra nos detalhes técnicos do que acontece quando você instala um plug-in.)

Em seguida, inclua um única linha — static searchable = true — em grails-app/domain/Entry.groovy, conforme mostrado na Lista 2:


Lista 2. Tornando a Classe Entry Pesquisável

class Entry {
  static searchable = true

  static constraints = {
    title()
    summary(maxSize:1000)
    filename(blank:true, nullable:true)
    dateCreated()
    lastUpdated()
  }

  static mapping = {
    sort "lastUpdated":"desc"
  }

  static belongsTo = [author:User]

  String title
  String summary
  String filename
  Date dateCreated
  Date lastUpdated
}

Observe que é necessário tornar as classes de domínio explicitamente pesquisáveis. Isso significa que é possível continuar a manter os dados de infraestrutura, como logins e senhas, armazenados na classe User oculta ao olho público. (A documentação on-line para Searchable fornece informações adicionais sobre como especificar as classes e campos a serem incluídos no índice; consulte Recursos.)

Com essa linha de código, todo o poder de Lucene e Compass foi incluído no Blogito. Digite grails run-app para iniciar o aplicativo e visite então http://localhost:9090/blogito/searchable. Digite um termo de procura, como grails, e verifique os resultados, conforme mostrado na Figura 1:


Figura 1. Resultados de Procura Padrão

Claramente, alguns resultados foram localizados, mas as ocorrências são menos do que descritivas. Para remediar isso, inclua um método toString() em Entry.groovy, conforme mostrado na Lista 3:


Lista 3. Incluindo toString() em Entry

class Entry {
  static searchable = true

  //snip

  String toString(){
    "${title} (${lastUpdated})"
  }
}

Procure grails novamente. Os resultados devem ser ligeiramente mais fáceis e simples, conforme mostrado na Figura 2:


Figura 2. Os Resultados da Procura com um Método toString()

Agora que você está convencido de que a funcionalidade bruta do plug-in Searchable está em vigor, está pronto para a próxima etapa: integrá-lo profundamente a seu aplicativo.



Voltar para parte superior


Explorando a Infraestrutura dos Plug-ins

Dando uma olhada nos diretórios do Blogito, não parece haver nenhum arquivo novo. Se puder visitar http://localhost:9090/blogito/searchable em um navegador da Web, deveria haver um arquivo grails-app/controllers/SearchableController.groovy. Estranhamente, não está lá. Provavelmente, deveria haver alguns JARs do Lucene e do Compass no diretório lib, mas está tão vazio quanto quando você digitou grails create-app pela primeira vez para iniciar o projeto. Na verdade, a única mudança no Blogito é uma única linha nova em application.properties, conforme mostrado na Lista 4:


Lista 4. application.properties, Mostrando o Novo Plug-in Searchable Instalado

#utf-8
#Wed Jun 24 15:41:16 MDT 2009
app.version=0.4
app.servlet.version=2.4
app.grails.version=1.1.1
plugins.searchable=0.5.5
plugins.hibernate=1.1.1
app.name=blogito

Com base na linha plug-ins.searchable , você sabe que o Blogito tem conhecimento do plug-in Searchable. Então, onde está toda a funcionalidade oculta? Para descobrir, é necessário apenas voltar à saída da tela que surgiu rapidamente quando o plug-in foi instalado pela primeira vez. Vou direcioná-lo em detalhes agora.

Ao digitar grails install-plugin searchable, a primeira coisa que ocorre é que um pedido passa pela Web para obter as listas de plug-ins mais recentes. A Lista 5 mostra os detalhes:


Lista 5. Fazendo Download das Listas Principais de Plug-ins

$ grails install-plugin searchable
//snip

Reading remote plugin list ...
  [get] Getting: http://svn.codehaus.org/grails/trunk/grails-plugins/
      .plugin-meta/plugins-list.xml
  [get] To: /Users/sdavis/.grails/1.1.1/plugins-list-core.xml
  [get] last modified = Mon Jun 22 04:16:31 MDT 2009

Reading remote plugin list ...
  [get] Getting: http://plugins.grails.org/.plugin-meta/plugins-list.xml
  [get] To: /Users/sdavis/.grails/1.1.1/plugins-list-default.xml
  [get] last modified = Wed Jun 24 06:51:24 MDT 2009

As duas listas — principal e padrão — fornecem metadados sobre os plug-ins, incluindo autor, descrição e número de versão. O mais importante é que esse é o momento em que o Grails descobre a URL para o arquivo ZIP que realmente contém o plug-in. A Lista 6 mostra informações sobre o plug-in Hinernate do arquivo plugins-list-core.xml:


Lista 6. Descrevendo o Plug-in Hibernate

<plugins revision="9011">
  <plugin latest-release="1.1.1" name="hibernate">
    <release tag="RELEASE_1_1" type="svn" version="1.1">
      <title>Hibernate for Grails</title>
      <author>Graeme Rocher</author>
      <authorEmail/>
      <description>A plugin that provides integration between
        Grails and Hibernate through GORM</description>
      <documentation>http://grails.org/doc/$version</documentation>
        <file>http://svn.codehaus.org/grails/trunk/grails-plugins/
          grails-hibernate/tags/RELEASE_1_1/grails-hibernate-1.1.zip
        </file>
    </release>
    <!-- snip -->
  </plugin>
</plugins>

Atualmente, o plug-in Hibernate é o único listado no arquivo de plug-ins principal. Essa lista contém plug-ins necessários — funcionalidade sem a qual Grails não pode ser executado. A lista padrão contém plug-ins opcionais de contribuição da comunidade.

Você observou na Lista 5 onde esses arquivos estão armazenados? Há um diretório .grails criado em seu diretório inicial (/Users/nome do usuário nos sistemas semelhantes ao UNIX® ou C:\Documents and Settings\nome do usuário no Windows®). Esse diretório é onde as classes compiladas são armazenadas quando você digita grails run-app. Ao digitar grails clean, o diretório application sob projects é excluído. Mas como pode ver, .grails é também onde os plug-ins transferidos por download estão armazenados. Abra .grails/1.1.1/plugins-list-default.xml em um editor de texto e procure a entrada para o plug-in Searchable. A Lista 7 mostra os detalhes:


Lista 7. Descrevendo o Plug-in Searchable

<plugin latest-release="0.5.5" name="searchable">
  <release tag="RELEASE_0_5_5" type="svn" version="0.5.5">
    <title>Adds rich search functionality to Grails domain models.
      This version is recommended for JDK 1.5+</title>
    <author>Maurice Nicholson</author>
    <authorEmail>maurice@freeshell.org</authorEmail>
    <description>Adds rich search functionality to Grails domain models.
      Built on Compass (http://www.compass-project.org/) and Lucene
      (http://lucene.apache.org/)
      This version is recommended for JDK 1.5+
    </description>
    <documentation>http://grails.org/Searchable+Plugin</documentation>
    <file>http://plugins.grails.org/grails-searchable/
      tags/RELEASE_0_5_5/grails-searchable-0.5.5.zip</file>
  </release>

  <!-- snip -->
</plugin>

Quando o Grails souber de onde o plug-in pode ser transferido por download, ele (não surpreendentemente) faz download do plug-in solicitado para .grails/1.1.1/plugins, conforme mostrado na Lista 8:


Lista 8. Fazendo Download do Plug-in

$ grails install-plugin searchable
  //download core and default plugin lists

  // continued...
  [get] Getting: http://plugins.grails.org/grails-searchable/
  tags/RELEASE_0_5_5/grails-searchable-0.5.5.zip
  [get] To: /Users/sdavis/.grails/1.1.1/plugins/grails-searchable-0.5.5.zip
  [get] last modified = Thu Jun 18 22:24:45 MDT 2009

Por fim, o Grails copia e descompacta o plug-in do cache local para seu projeto, conforme mostrado na Lista 9:


Lista 9. Incluindo o Plug-in em seu Projeto

$ grails install-plugin searchable
  //download core and default plugin lists
  //download requested plugin

  // continued...
  [copy] Copying 1 file to /Users/sdavis/.grails/1.1.1/projects/blogito/plugins
  Installing plug-in searchable-0.5.5
  [mkdir] Created dir:
     /Users/sdavis/.grails/1.1.1/projects/blogito/plugins/searchable-0.5.5
  [unzip] Expanding:
     /Users/sdavis/.grails/1.1.1/plugins/grails-searchable-0.5.5.zip into
     /Users/sdavis/.grails/1.1.1/projects/blogito/plugins/searchable-0.5.5

Informações adicionais

É possível localizar informações adicionais sobra a infraestrutura do plug-in nas notas sobre o release para o Grails 1.1 (consulte Recursos). Você aprenderá como instalar plug-ins de forma global (de forma que cada novo projeto criado inclua automaticamente os plug-ins específicos), incluir repositórios de plug-ins alternativos na lista, limitar plug-ins para executar somente em ambientes específicos ou somente para scripts específicos de linha de comando do Grails e muito mais.

Antes de seguir muito adiante, certifique de que isto faça sentido para você. A linha em application.properties corresponde ao diretório descompactado de seu diretório de projeto em .grails. Isso significa que para desinstalar um plug-in, é possível digitar grails uninstall-plugin myplugin ou simplesmente remover a linha de application.properties e manualmente excluir o diretório de seu diretório de projeto em .grails.

Saber que plug-ins são passados como simples arquivos ZIP também é valioso. No próximo artigo, mostrarei como criar seu próprio plug-in e instalá-lo a partir de um arquivo ZIP local (grails install-plugin myplugin /local/path/to/myplugin.zip). É possível instalar até mesmo um plug-in a partir de uma URL remota — grails install-plugin myplugin http://somewhere.com/myplugin.zip.



Voltar para parte superior


Explorando o Plug-in Searchable

Agora que você sabe onde o plug-in Searchable está instalado (.grails/1.1.1/projects/blogito/plugins/searchable-0.5.5), dê uma olhada por lá. A estrutura de diretórios (mostrada na Figura 3) deve parecer familiar — plug-ins e aplicativos compartilham o mesmo layout básico:


Figura 3. A Estrutura de Diretórios de um Layout

O SearchableController está exatamente onde você esperaria que estivesse: grails-app/controllers. Abra o arquivo em um editor de texto. A Lista 10 mostra uma parte do código de origem:


Lista 10. SearchableController

import org.compass.core.engine.SearchEngineQueryParseException
class SearchableController {
  def searchableService

  def index = {
    if (!params.q?.trim()) {
      return [:]
    }
    try {
      return [searchResult: searchableService.search(params.q, params)]
    } catch (SearchEngineQueryParseException ex) {
      return [parseException: true]
    }
  }

  //snip
}

Como é possível perceber, SearchableService é injetado no controlador logo após a declaração de classe. A ação index familiar é o destino padrão. Se o parâmetro q não for passado, um hashmap vazio é retornado para grails-app/views/searchable/index.gsp. Baseado na lógica da visualização, isso exibe uma página vazia.

Por volta da linha 100 do index.gsp, deve-se encontrar o formulário que configura o parâmetro q e, recursivamente, envia-se de volta à ação index . A Lista 11 mostra o formulário:


Lista 11. O Formulário Pesquisável em index.gsp

<g:form url='[controller: "searchable", action: "index"]'
        id="searchableForm"
        name="searchableForm"
        method="get">
  <g:textField name="q" value="${params.q}" size="50"/>
  <input type="submit" value="Search" />
</g:form>

Dando uma olhada na Lista 10, é possível perceber que quando o parâmetro q possui um termo de procura, o resultado da chamada searchableService.search() é retornado para index.gsp. Em torno da linha 150 do index.gsp, os resultados são exibidos conforme mostrado na Lista 12:


Lista 12. Exibindo os Resultados de Procura

<g:if test="${haveResults}">
  <div class="results">
    <g:each var="result" in="${searchResult.results}" status="index">
      <div class="result">
        <g:set var="className" value="${ClassUtils.getShortName(result.getClass())}" />
        <g:set var="link"
               value="${createLink(controller: className[0].toLowerCase() +
                 className[1..-1],
               action: 'show',
               id: result.id)}" />
        <div class="name"><a href="${link}">${className} #${result.id}</a></div>
        <g:set var="desc" value="${result.toString()}" />
        <g:if test="${desc.size() > 120}">
          <g:set var="desc" value="${desc[0..120] + '...'}" />
        </g:if>
        <div class="desc">${desc.encodeAsHTML()}</div>
        <div class="displayLink">${link}</div>
      </div>
    </g:each>
  </div>

  <!-- snip -->
</g:if>

Incentivo-o a explorar o plug-in Searchable em mais detalhes. Verifique grails-app/services/SearchableService.groovy. Observe os JARs do Lucene e do Compass incluídos no diretório lib. Percorra os diretórios src/java e src/groovy para ver todas as classes de suporte. Revise GroovyTestCases no diretório tests. Todas as partes de um aplicativo Grails típico estão aqui neste plug-in.

Toda vez que você instalar um novo plug-in, deve dar uma olhada superficial na implementação. Isso ajudará a identificar todas as partes móveis, ver como elas funcionam juntas e — o mais importante — fornecer dicas de como é possível incorporá-las mais profundamente a seu aplicativo. Na próxima seção, você verá como mover recursos de procura da implementação padrão para componentes customizados próprios.



Voltar para parte superior


Incorporando a Procura Mais Profundamente ao Blogito

Em seguida é mostrado como é possível incluir sua própria procura para Entries. Para iniciar, abra grails-app/controllers/EntryController.groovy em um editor de texto. Inclua uma ação simples denominada search, conforme mostrado na Lista 13. (Não se esqueça de permitir que usuários não-autorizados procurem entradas de blog incluindo a ação search em beforeInterceptor.)


Lista 13. Incluindo a Ação search

class EntryController {

  def beforeInterceptor =
     [action:this.&auth, except:["index", "list", "show", "atom", "search"]]

  def search = {
    render Entry.search(params.q, params)
  }

  //snip
}

O SearchableService, conforme demonstrado na seção anterior, é ótimo para realizar procuras em todo o site em todas as suas classe de domínio. Mas o plug-in Searchable também realiza um pouco de metaprogramação em suas classes de domínio individuais. Exatamente como o Grails inclui dinamicamente os métodos list(), get() e findBy() , o plug-in Searchable inclui um método search() .

Teste sua nova ação search digitando http://localhost:9090/blogito/entry/search?q=groovy em seu navegador da Web. Deve-se ver um hashmap de resultados, muito parecido com os da Figura 4:


Figura 4. Exibindo os Resultados de Procura Brutos

Agora que você sabe que o método search() funciona, a próxima etapa é tornar a interface com o usuário um pouco mais fácil e simples. Crie um modelo parcial denominado _search.gsp em grails-app/views/layouts. Inclua o código na Lista 14:


Lista 14. O Modelo Parcial de Procura

<div id="search">
  <g:form url='[controller: "entry", action: "search"]'
          id="searchableForm"
          name="searchableForm"
          method="get">
    <g:textField name="q" value="${params.q}" />
    <input type="submit" value="Search" />
  </g:form>
</div>

Observe que o controlador está configurado para entry e a ação está configurada para search.

Em seguida, está na hora de exibir o modelo parcial. Abra grails-app/views/layouts/_header.gsp em um editor de texto e inclua uma tag render , conforme mostrado na Lista 15:


Lista 15. Incluindo o Modelo de Procura no Cabeçalho

<g:render template="/layouts/search" />

<div id="header">
  <p><g:link class="header-main" controller="entry">Blogito</g:link></p>
  <p class="header-sub">
    <g:link controller="entry" action="atom">
    <img src="${createLinkTo(dir:'images',file:'feed-icon-28x28.png')}"
        alt="Subscribe" title="Subscribe"/>
    </g:link>
    A tiny little blog
  </p>

  <div id="loginHeader">
    <g:loginControl />
  </div>
</div>

Inclua um pouco de Cascading Style Sheets (CSS) em web-app/css/main.css para assegurar que search <div> flutue no canto direito superior da tela, conforme mostrado na Lista 16:


Lista 16. CSS para Ajustar a Posição do Formulário de Procura

#search {
  float: right;
  margin: 2em 1em;
}

Com todas as mudanças da visualização em vigor, atualize seu navegador. Sua tela deve ter a aparência da Figura 5:


Figura 5. Incluindo o Formulário de Procura no Cabeçalho

A última coisa que se deve fazer é renderizar os resultados de search em HTML em vez de a saída de depuração simples em execução no momento. Ajuste a ação search em EntryController conforme mostrado na Lista 17:


Lista 17. Uma Ação de Procura Mais Robusta

def search = {
  //render Entry.search(params.q, params)
  def searchResults = Entry.search(params.q, params)
  flash.message = "${searchResults.total} results found for search: ${params.q}"
  flash.q = params.q
  return [searchResults:searchResults.results, resultCount:searchResults.total]
}

Como a ação é denominada search, deve-se criar o arquivo search.gsp correspondente, mostrado na Lista 18, em grails-app/views/entry:


Lista 18. Search.gsp

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="layout" content="main" />
    <title>Blogito</title>
  </head>
  <body>
    <g:if test="${flash.message}">
      <div class="message">${flash.message}</div>
    </g:if>

    <div class="body">
      <div class="list">
        <g:each in="${searchResults}" status="i" var="entry">

        <div class="entry">
          <h2>
            <g:link action="show"
                    id="${entry.id}">${entry.title}</g:link>
          </h2>
          <p>${entry.summary}</p>
        </div>

        </g:each>
      </div>
    </div>
    <div class="paginateButtons">
      <g:paginate total="${resultCount}" params="${flash}"/>
    </div>
  </body>
</html>

Procure grails uma última vez em seu navegador da Web. Os resultados devem ter a aparência daqueles da Figura 6:


Figura 6. Bons Resultados de procura em HTML



Voltar para parte superior


Conclusão

Plug-ins são uma parte emocionante e vibrante existente do ecossistema Grails. Permitem combinar uma ampla variedade de funcionalidades pré-existentes. Quando entender onde estão os pontos de contato com seu código base (application.properties e o diretório .grails), é possível explorar o código de origem para entender melhor como o autor do plug-in implementou a mágica, assim como encontrar inspiração para uma integração mais profunda com seu próprio código.

Da próxima vez, mostrarei como criar um plug-in próprio. Até lá, divirta-se dominando Grails.



Recursos

Aprender
  • Dominando Grails: Leia mais desta série para obter maiorconhecimento sobre Grails e tudo que pode ser feito com ele.

  • Grails: Visite o Web site do Grails.

  • Plug-ins do Grails: Visite o portal de plug-ins do Grails para obter informações sobre os plug-ins mais recentes disponíveis para a estrutura do Grails.

  • Searchable: Aqui será encontrada documentação para o plug-in Searchable plug-in.

  • Notas sobre o Release do Grails 1.1: Se aprofunde mais na infraestrutura de plug-ins.

  • Lucene e Compass: O plug-in Searchable é baseado em software de procura desenvolvido por esses dois projetos.

  • Grails Framework Reference Documentation: A bíblia do Grails.

  • Groovy Recipes (Scott Davis, Pragmatic Programmers, 2008): Aprenda mais sobre Groovy e Grails no livro mais recente de Scott Davis.

  • Praticamente Groovy: Essa série do developerWorks é dedicada a explorar os usos práticos de Groovy e ensinar quando e como aplicá-los com êxito.

  • Groovy: Aprenda mais sobre Groovy no Web site do projeto.

  • AboutGroovy.com: Mantenha-se atualizado com os links de notícias e artigos mais recentes do Groovy.

  • Livraria de tecnologia: Procure livros sobre esses e outros tópicos técnicos.

  • Zona de Tecnologia Java do developerWorks: Encontre centenas de artigos sobre cada aspecto de programação Java.


Obter produtos e tecnologias
  • GrailsFaça download do release mais recente do Grails.

  • Blogito: É possível fazer download do aplicativo Blogito completo.

Discutir


Sobre o autor

Scott Davis photo

Scott Davis is a Senior IT Architect with the Portals, Content Management, and e-Commerce practice of IBM Global Services. He has been architecting and developing solutions for over 13 years, with the last 5 focused on Portals and Content Management systems. He holds a computer science degree from the University of Colorado at Boulder.




Avalie esta página


Reserve um instante para completar este formulário para nos ajudar a servi-lo melhor.



 


 


Não
são úteis
Extremamente
úteis
 






Voltar para parte superior


Java e todas as marcas registradas baseadas em Java são marcas registradas da Sun Microsystems, Inc. nos Estados Unidos e/ou em outros países. Microsoft, Windows, Windows NT e o logotipo do Windows são marcas registradas da Microsoft Corporation nos Estados Unidos e/ou em outros países. UNIX é uma marca registrada do The Open Group nos Estados Unidos e em outros países. Outros nomes de empresas, produtos e serviços podem ser marcas registradas ou marcas de serviço de terceiros.