XML: The bridge between GWT and PHP

GWT pode usar serviços PHP e XML fornece uma fácil ponte de troca de dados

Os aplicativos do Google Web Toolkit (GWT), além de conectarem a servlets da maneira tradicional Java™ , também podem usar serviços da Web PHP para enviar e receber dados em XML. Você irá explorar métodos para gerar documentos XML e processá-los, na linguagem Java e em PHP.

Federico Kereki, Systems Engineer, 自由职业者

Photo of Federico KerekiFederico Kereki é um engenheiro de sistemas uruguaio com mais de 20 anos de experiência desenvolvendo sistemas, realizando trabalho de consultoria e ensinando nas universidades. Atualmente, trabalha com uma boa mistura de acrônimos: SOA, GWT, Ajax, PHP e, é claro, FLOSS! É possível contatar Frederico em fkereki@gmail.com.


nível de autor Contribuidor do
        developerWorks

07/Abr/2009

Acrônimos Usados Frequentemente

  • Ajax: Asynchronous JavaScript + XML
  • PEAR: PHP Extension and Application Repository
  • RPC: Chamada de procedimento remoto
  • RSS: Really Simple Syndication
  • W3C: World Wide Web Consortium
  • XML: Linguagem de Marcação Extensível

GWT permite fácil acesso aos servlets do lado do servidor programados na linguagem Java e os dados são passados de forma transparente, nos bastidores, entre cliente e servidor. No entanto, à medida que trabalha com GWT, não está limitado a se comunicar com esses servlets e você pode trocar dados livremente com todos os tipos de serviços da Web. Em muitos casos (para serviços simples), essas transferências podem ser feitas com texto simples, mas sempre que os dados forem estruturados ou simplesmente mais complicados (pense em RSS, por exemplo), as chances são que XML os representará.

Este artigo examina um aplicativo GWT simples e dois serviços da Web PHP, mostrando diversas maneiras diferentes de produzir e consumir documentos XML. Este artigo não tem a intenção de ser um tutorial ou um manual completo, mas ser, na verdade, um conjunto de ponteiros para que você possa começar mais rapidamente a trabalhar com XML como uma ponte entre GWT e PHP por conta própria.

Um Aplicativo de Teste

JSON: Outra Alternativa Viável

Originalmente parte da linguagem JavaScript™ , JavaScript Object Notation (JSON) eventualmente transformou-se de forma independente em uma alternativa válida completa para XML. JSON fornece um formato baseado em texto simples e legível para representar arrays e objetos. Além do mais, é seguro dizer que as representações XML e JSON para os mesmos dados podem ter tamanho semelhante. Diversos sites conhecidos (como Google ou Yahoo!) fornecem JSON, assim como XML.

Uma vantagem de JSON é que JavaScript pode processá-lo de forma bastante rápida (por exemplo, pode converter JSON em um objeto com uma única instrução), o que o torna atraente para desenvolvedores da Web. Como GWT compila todo o código do lado do cliente em código JavaScript, é lógico que GWT fornece uma boa biblioteca para ele e todos os exemplos deste artigo também poderiam ter sido programados com JSON em vez de XML. Verifique a seção Recursos para obter links para informações adicionais sobre JSON.

Para mostrar como é possível usar XML como uma ponte entre PHP e GWT, forneço um aplicativo simples baseado em dados de países/regiões/cidades. Verificando o código e criação do banco de dados na Lista 1, é possível ver que:

  • Países têm um código exclusivo (por exemplo, UY para Uruguai) e um nome.
  • Países são divididos em regiões, identificadas com um código (exclusivo dentro do país) e têm um nome.
  • Regiões têm cidades, que têm um nome (ASCII puro), um nome acentuado (que pode conter caracteres estrangeiros), uma população (ou 0 se desconhecida), uma latitude e uma longitude. O nome da cidade pode aparecer em diferentes regiões do mesmo país.
Listing 1. Database creation code
CREATE DATABASE world
    DEFAULT CHARACTER SET latin1
    COLLATE latin1_general_ci;

USE world;

CREATE TABLE countries (
    countryCode char(2) NOT NULL,
    countryName varchar(50) NOT NULL,
    PRIMARY KEY (countryCode)
    KEY countryName (countryName)
    );

CREATE TABLE regions (
    countryCode char(2) NOT NULL,
    regionCode char(2) NOT NULL,
    regionName varchar(50) NOT NULL,
    PRIMARY KEY (countryCode,regionCode),
    KEY regionName (regionName)
    );

CREATE TABLE cities (
    countryCode char(2) NOT NULL,
    cityName varchar(50) NOT NULL,
    cityAccentedName varchar(50) NOT NULL,
    regionCode char(2) NOT NULL,
    population bigint(20) NOT NULL,
    latitude float(10,7) NOT NULL,
    longitude float(10,7) NOT NULL,
    KEY `INDEX` (countryCode,regionCode,cityName),
    KEY cityName (cityName),
    KEY cityAccentedName (cityAccentedName)
    );

Criei um projeto GWT simples com apenas um formulário e dois serviços da Web PHP. (Consulte Downloads para obter o código de origem integral.) Ao iniciar o aplicativo, verá a janela simples da Figura 1.

Figura 1. O Formulário Vazio
Uma Captura de Tela de um Formulário Vazio para os Aplicativos de Amostra

O formulário GWT permite inserir uma parte de um nome de cidade e chama um serviço PHP para obter todas as cidades que correspondem a o que foi digitado. As cidades são exibidas em uma grade e é possível editar os campos de população, latitude e longitude. Os dados editados podem ser enviados de volta a um segundo serviço da Web PHP, que atualiza o banco de dados. Todas as transferências de dados são feitas em XML. Como 2009 marca 200 anos do nascimento de Charles Darwin e 150 anos de seu livro A Origem das Espécies , poderia procurar cidades com DARWIN em seus nomes; consulte a Figura 2 para obter os resultados.

Figura 2. Uma Procura por Cidades que Incluem "Darwin" em seus Nomes
Um Formulário de Amostra, Após Obter as Informações das Cidades

Alguma Configuração Extra

Apenas para referência, trabalhei com:

  • GWT versão 1.5.3
  • PHP versão 5.2.8
  • Servidor de banco de dados MySQL® versão 5.0.67
  • Apache versão 2.2.10 sob OpenSUSE® versão 11.1

Instalei todo o software com instalação padrão, mas GWT precisava de uma etapa extra de configuração para que eu pudesse testar a conexão GWT-PHP; verifique a barra lateral O Problema SOP para ver a razão. Para desativar verificações same-origin policy (SOP) no navegador interno do GWT, edite o arquivo ./mozilla-1.7.12/greprefs/all.js em seu diretório do GWT e inclua as linhas da Lista 2 no final desse arquivo:

Listing 2. Configuration changes for the internal GWT browser
pref("capability.policy.default.XMLHttpRequest.abort", "allAccess");
pref("capability.policy.default.XMLHttpRequest.getAllResponseHeaders","allAccess");
pref("capability.policy.default.XMLHttpRequest.getResponseHeader","allAccess");
pref("capability.policy.default.XMLHttpRequest.open", "allAccess");
pref("capability.policy.default.XMLHttpRequest.send", "allAccess");
pref("capability.policy.default.XMLHttpRequest.setRequestHeader","allAccess");
pref("capability.policy.default.XMLHttpRequest.onreadystatechange","allAccess");
pref("capability.policy.default.XMLHttpRequest.readyState", "allAccess");
pref("capability.policy.default.XMLHttpRequest.responseText","allAccess");
pref("capability.policy.default.XMLHttpRequest.responseXML","allAccess");
pref("capability.policy.default.XMLHttpRequest.status", "allAccess");
pref("capability.policy.default.XMLHttpRequest.statusText", "allAccess");

O Problema SOP

SOP é uma restrição de segurança que basicamente proíbe que uma página seja carregada de uma determinada origem (o que significa o trio protocolo/host/porta da URL) para acessar dados de uma origem diferente. (O Windows® Internet Explorer® é bastante cavalheiro em relação a SOP e irá ignorar as mudanças de porta, mas esse não é o padrão.) Por exemplo, seu seu Web client GWT tiver sido carregado de http://www.yoursite.com:80/some/page/at/your/site, SOP não permitirá que seu cliente obtenha dados que não sejam da mesma URL, bloqueando chamadas para https://www.yoursite.com (protocolo diferente, http://othersite.com (host diferente) e até mesmo para http://www.yoursite.com:81 (porta diferente).

SOP é uma boa ideia, pois torna impossível código JavaScript trapaceiro de uma determinada origem acessar e manipular dados obtidos de outra origem. Na verdade, não permitir SOP seria o último desejo de phishers: Enquanto olha uma página valida legítima, um terceiro monitora. Com SOP ativado, pode ficar tranquilo que o que você visualizar foi enviado pela origem esperada; não pode haver nenhum código de outras origens (possivelmente suspeitas).

Em contraste, para desenvolvedores de GWT, SOP é um problema. Ao avaliar ser aplicativo em modo Hospedado, ele conecta à porta 8888, mas os serviços PHP que desejará acessar com certeza residirão na porta 80 padrão, portanto, SOP rejeitará a chamada. (É claro que após implementar seu aplicativo no modo Compilado, será executado perfeitamente, pois é executado a partir da porta padrão 80, também, respeitando SOP.) Você não deseja acessar todos os tipos de sites; deseja acessar apenas uma porta diferente na mesma origem, mas SOP não permite.

Sempre que atualizar GWT, será necessário fazer essa mudança novamente. Além disso, observe que, sem fazer isso, pode ser que seja gravado código que falha no modo Hospedado, mas é executado corretamente no modo Compilado; após a mudança, pode haver código executado no modo Hospedado, mas que falha no modo Compilado, fique ligado!

Enviando XML com PHP

Esse aplicativo simplesmente define um formulário simples, com alguns rótulos, uma caixa de texto, dois botões de comando e uma grade para os resultados. Sempre que clicar em Obter Cidades, o aplicativo chama um serviço PHP para obter um documento XML com todas as cidades que correspondem ao que foi digitado na caixa de texto. A Lista 3 mostra um XML de amostra (um tanto quanto abreviado) que é enviado p serviço PHP para o aplicativo GWT. O código XML produzido deve ilustrar diversos recursos XML e é, portanto, muito mais longo do que seria para um aplicativo real. Geralmente, um serviço XML bem projetado usa menos tags e mais atributos, evita deslocamento e é um documento mais curto.

Listing 3. The XML document produced when searching for "tokyo"
<?xml version="1.0" encoding="UTF-8"?>
 <cities>
  <city name="tokyo">
   <country code="JP" name="Japan"/>
   <region code="40" name="Tokyo"/>
   <coords>
    <lat>35.6850014</lat>
    <lon>139.7513885</lon>
   </coords>
   <pop>31480498</pop>
  </city>
  <city name="tokyo">
   <country code="PG" name="Papua New Guinea"/>
    <region code="01" name="Central"/>
   <coords>
    <lat>-8.3999996</lat>
    <lon>147.1499939</lon>
   </coords>
  </city>
  <city name="tokyojitori">
   <country code="KR" name="Korea, Republic of"/>
   <region code="16" name="Cholla-namdo"/>
   <coords>
    <lat>34.2380562</lat>
    <lon>125.9394455</lon>
   </coords>
  </city>
</cities>

Há duas versões do próprio serviço PHP —getcities1.php e getcities2.php—cada um dos quais mostra diferentes maneiras de produzir XML.

De longe, a maneira mais simples de produzir XML é simplesmente imprimir o texto apropriado de forma sequencial ou construir uma cadeia de caracteres, em seguida, emiti-lo usando echo. O tipo de conteúdo deve ser configurado para text/xml de forma que seja reconhecido corretamente. Lembre-se também de incluir uma linha de descrição apropriada que especifica a versão XML e a codificação de dados. Será necessário escapar cadeias de caracteres de forma que não incluam caracteres menor que (<), maior que (>) ou e comercial (&); a maneira mais fácil é usar a função PHP htmlspecialchars() . A codificação é fácil, como a parte da Lista 4 mostra. Observe que deslocamento e quebras de linhas não são realmente necessários, apesar de facilitarem a leitura do código.

Listing 4. The simplest method to produce XML from a PHP service
...
header("Content-type: text/xml");
...
echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";
echo '<cities>'."\n";
...
while ($row= mysql_fetch_assoc($result)) {
  echo ' <city name="'.htmlspecialchars($row['cityName']).'">'."\n";
  echo '  <country code="'.$row['countryCode'].'" ';
  echo 'name="'.htmlentities($row['countryName']).'"/>'."\n";
  echo '  <region code="'.$row['regionCode'].'" ';
  echo 'name="'.htmlentities($row['regionName']).'"/>'."\n";

  echo '  <coords>'."\n";
  echo '    <lat>'.$row['latitude'].'</lat>'."\n";
  echo '    <lon>'.$row['longitude'].'</lon>'."\n";
  echo '  </coords>'."\n";

  if ($row['population']>0) {
    echo '  <pop>'.$row['population'].'</pop>'."\n";
  }

  echo ' </city>'."\n";
}
echo '</cities>'."\n";

Um segundo método (e não muito mais longo) para produzir código XML é usando XMLWriter. (Uma classe companheira, XMLReader, permite processamento de XML.) Pode esquecer sobre escapar caracteres, pois isso é feito automaticamente. Apesar de parecer conter mais palavras do que o método echo() acima, pode ser argumentado que essa abordagem cria um código mais compreensível. Observe especialmente o uso do protocolo php://output para que o texto seja emitido como com o comando echo . (Consulte a Lista 5.)

Listing 5. XMLWriter provides simple methods to build up an XML document one element at a time
...
$writer= new XMLWriter();
$writer->openURI('php://output')
$writer->startDocument('1.0', 'UTF-8');
$writer->startElement("cities");

while ($row= mysql_fetch_assoc($result)) {
  $writer->startElement("city");
  $writer->writeAttribute("name", $row['cityName']);

  $writer->startElement("country");
  $writer->writeAttribute("code", $row['countryCode']);
  $writer->writeAttribute("name", $row['countryName']);
  $writer->endElement();

  $writer->startElement("region");
  $writer->writeAttribute("code", $row['regionCode']);
  $writer->writeAttribute("name", $row['regionName']);
  $writer->endElement();

  $writer->startElement("coords");
  $writer->writeElement("lat", $row['latitude']);
  $writer->writeElement("lon", $row['longitude']);
  $writer->endElement();

  if ($row['population']>0) {
    $writer->writeElement("pop", $row['population']);
  }

  $writer->endElement();	// city
}
$writer->endElement(); // cities
...

Se quiser fazer experiências com mais métodos de produção de XML, PHP certamente tem muito a oferecer. Por exemplo, é possível usar SimpleXML; ser;a usado posteriormente para ler XML, mas também pode ser usado para criação de documento XML. Além disso, você também pode dar uma olhada na estrutura PEAR, que inclui diversas classes para fácil geração de XML. (Consulte Recursos para obter links para informações adicionais.)

Processando XML com GWT

GWT fornece somente XMLParser (no pacote com.google.gwt.xml.client ) para leitura e gravação e XML. Use o método parse() para criar um Document, em seguida, use getDocumentElement() para obter seu elemento raiz; em seguida, estará pronto para começar a percorrer o XML.

Um ponto importante é que o método removeWhitespace() deve ser usado para remover espaço em branco de seu documento. Analisadores de navegador às vezes criam nós de texto vazios correspondentes a guias ou quebras de linha e, caso não sejam removidos, sei processo encontrará elementos irrelevantes inesperados, que podem afetar sua lógica (consulte Lista 6). Outro ponto: Se esperar seções CDATA, seria necessário verificar se seu navegador aceita o método supportsCDATASection() ; do contrário, essas seções produzirão nós de texto, em vez disso. Verifique a documentação de GWT (consulte Recursos) para saber mais sobre isso.

Listing 6. XMLParser provides for both XML reading and creation in GWT
protected void loadCities(final String xmlCities) {
  ...
  final Document xmlDoc= XMLParser.parse(xmlCities);
  final Element root= xmlDoc.getDocumentElement();
  XMLParser.removeWhitespace(xmlDoc);

  final NodeList cities= root.getElementsByTagName("city");
  for (int i= 0; i < cities.getLength(); i++) {
    final Element city= (Element)cities.item(i);
    // show city.getAttributeNode("name").getValue()

    final Element country= (Element)city.getElementsByTagName("country").item(0);
    // show country.getAttributeNode("code").getValue()
    // show country.getAttributeNode("name").getValue()
    ...
    final Element population= (Element)city.getElementsByTagName("pop").item(0);
    if (population != null) {
      // show population.getFirstChild().getNodeValue()
    }

    final Element coords= (Element)city.getElementsByTagName("coords").item(0);
    final Element lat= (Element)coords.getElementsByTagName("lat").item(0);
    // show lat.getFirstChild().getNodeValue()
    ...
  }
...

É possível obter elementos repetidos (como city neste exemplo) usando o método getElementsByTagName() e percorrendo a array resultante. Uma alternativa é usar o método getFirstChild() , em seguida, percorrer os demais elementos no mesmo nível com getNextSibling(). Obter atributos requer, primeiramente, o método getAttributeNode() , em seguida, o método getValue() . Há métodos que processam seções CDATA, comentários e todos os componentes XML possíveis.

Enviando XML com GWT

O aplicativo GWT permite que os usuários editem os campos de população, latitude e longitude,em seguida, enviem os dados das cidades de volta ao servidor para atualizar o banco de dados. Dois algoritmos são mostrados: um simples, construindo a cadeia de caracteres XML por partes e um algoritmo baseado em XMLParser que usa métodos específicos para criar a estrutura desejada.

O algoritmo mais simples é mostrado no método getCities1() . Um objeto String ou StringBuffer pode ser usado para construir o XML. Usei o primeiro, pois torna o código mais claro; mas, em termos de desempenho, a segunda opção é provavelmente melhor. Os mesmos problemas de escape de caractere visto na versão PHP estão aqui, portanto, use o método Html.htmlspecialchars() para corrigir isso. (Consulte Lista 7, que foi ligeiramente modificada por questão de clareza.)

Listing 7. Building XML piece by piece is simple using strings
protected String getCities1() {
  String result= "";

  result+= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  result+= "<cities>\n";

  for (all rows in the grid) {
    // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid

    result+= " <city name=\"" + Html.htmlspecialchars(cityName) + "\">\n";
    result+= "  <country code=\"" + countryCode + "\"/>\n";
    result+= "  <region code=\"" + regionCode + "\"/>\n";
    if (!pop.equals("0") && !pop.isEmpty()) {
      result+= "  <pop>" + pop + "</pop>\n";
    }

    result+= "  <coords>\n";
    result+= "   <lat>" + lat + "</lat>\n";
    result+= "   <lon>" + lon + "</lon>\n";
    result+= "  </coords>\n";
    result+= "</city>\n";
  }
  result+= "</cities>\n";
  return result;
}

O outro algoritmo simples para criação de XML é o método createDocument() da classe XMLParser . Em um estilo fortemente remanescente das funções SimpleXML em PHP, primeiro, cria-se um documento vazio, em seguida, inclui-se elementos nele. É possível criar todos os tipos de nós, assim como configurar valores de atributos. Por fim, o método toString() Java padrão produz uma representação do objeto; é necessário incluir apenas a linha de versão e codificação inicial e terá sua cadeia de caracteres necessária. (Consulte Lista 8, que foi modificada e resumida por questão de clareza.)

Listing 8. The XMLParser methods for XML creation are similar to SimpleXML's methods in PHP
protected String getCities2() {
  Document xml= XMLParser.createDocument();
  Element cities= xml.createElement("cities");
  xml.appendChild(cities);

  for (all rows in the grid) {
    // get cityName, countryCode, regionCode, pop, lat, and lon, from the grid

    Element city= xml.createElement("city");
    city.setAttribute("name", cityName);

    Element country= xml.createElement("country");
    country.setAttribute("code", countryCode);
    city.appendChild(country);
    ...
    if (!pop.equals("0") && !pop.isEmpty()) {
      Element popEl= xml.createElement("pop");
      Text popText= xml.createTextNode(pop);
      popEl.appendChild(popText);
      city.appendChild(popEl);
    }

    Element coords= xml.createElement("coords");
    Element lat= xml.createElement("lat");
    Text latText= xml.createTextNode(lat);
    lat.appendChild(latText);
    coords.appendChild(lat);
    ...
    city.appendChild(coords);
    cities.appendChild(city);
  }
  return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + xml.toString();
}

Lendo XML em PHP

Processar XML em PHP é um velho problema e há diversas soluções. No entanto, na minha opinião, nenhuma chega perto de SimpleXML em termos de clareza e concisão. Basicamente, cria-se um objeto PHP alimentando o documento XML ao método simplexml_load_string() , em seguida, atravesse o resultado usando operadores PHP padrão. Atributos tornam-se elementos de arrays (consulte como o código do país é lido na Lista 9, por exemplo) e elementos podem ser acessados como uma array (usando o método children() ) ou diretamente, como atributos do objeto.

Listing 9. SimpleXML is by far the easiest method of XML processing in PHP
$xml_str= $_POST["xmldata"];
$xml_obj= simplexml_load_string($xml_str);
...
foreach($xml_obj->children() as $city) {
  $name= addslashes($city['name']);
  $country= $city->country['code'];
  $region= $city->region['code'];
  $pop= $city->pop;
  $lat= $city->coords->lat;
  $lon= $city->coords->lon;

  mysql_query("REPLACE INTO cities ".
    "(cityName, countryCode, regionCode, population, latitude, longitude) VALUES (".
    "'{$name}', '{$country}', '{$region}', '{$pop}', '{$lat}', '{$lon}')");
}

Há muitas outras maneiras de processar XML com PHP. Verifique a seção Recursos para obter links.

Conclusão

Só arranhei a superfície das diversas maneiras que XML pode ser usado como uma ponte entre GWT e PHP, mas os métodos fornecidos devem suficientes para fornecer um ponto de partida. O ponto principal a se lembrar é que GWT não está limitado a usar seu próprio método RPC e também pode existir felizmente com XML, produzindo e consumindo esses documentos com facilidade.


Downloads

DescriçãoNomeTamanho
Java source code for this articlejava_source_code.zip4KB
PHP source code for this articlephp_source_code.zip4KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=396061
ArticleTitle=XML: The bridge between GWT and PHP
publish-date=04072009