sexta-feira, 24 de outubro de 2008

CIO

- Qual é a sua posição na empresa?
- C.I.O.
- Quer dizer que você fica na "posição de cio"?
- Isso não soou legal... ¬¬

/***********************************************************

C.I.O - Chief Information Officer

quarta-feira, 15 de outubro de 2008

Aprender banco de dados? É com mangás!

Ah, se já tivesse isso nos meus tempos de faculdade...



Mais no outro blog.

quinta-feira, 28 de agosto de 2008

Programador também é gente

Por isso, abrace o seu hoje.

Claro que eu prefiro abraçar uma colega programadora. =P



Via WTFBrasil.

quinta-feira, 26 de junho de 2008

Javascript é power, também com imagens

Que Javascript é poderoso, todo mundo que usa os aplicativos do Google deve saber (o Gmail é um bom exemplo), afinal, praticamente todos esses novos aplicativos Web 2.0, que exibem grande interação, usam AJAX e consequentemente, Javascript.

Mas e que tal aplicações menos... sérias? Já imaginou jogar no seu browser, totalmente feito em Javascript, clássicos como Wolfenstein 3D, Super Mario Bros. ou mesmo Super Mario Kart?

Jacob Seidelin, que tem o blog Nihilogic, não só pensou nisso, mas colocou as mãos na massa. Tá certo que os jogos não estão completos, mas só pelo fato de terem sido totalmente escritos em Javascript, é um feito e tanto!

Veja algumas imagens:

Super Mario Kart em Javascript:



Wolfenstein 3D, em Javascript:



Super Mario Bros. em apenas 14 Kb, em Javascript:




Além desses jogos, o cara, que deve ser fera em manipulação de imagens com Javascript, tem muitos outros projetos em seu blog. Como por exemplo, um pequeno editor de imagens: Pixastic : Photo editing with JavaScript and canvas.

Por causa das diferenças entre browsers, em especial o suporte ao canvas, alguns projetos do cara podem não rodar muito bem no Internet Explorer. Se bem que, com o Firefox 3, quem é que precisa do IE? =)

Post originalmente feito para o meu blog pessoal.

sexta-feira, 16 de maio de 2008

Hackeando o Caché - parte 2

No post anterior eu alertei sobre um bug em como o Caché lida com requisições HTTP para as suas páginas CSP (Caché Server Pages), bug este que pode ser explorado em um ataque de negação de Serviço (DoS, em inglês).

Neste post, detalharei não somente o bug, mas todas as circunstâncias que levaram a descoberta deste bug, suas consequências e detalhes técnicos.

Como tudo começou

Num dia como qualquer outro, de repente começam a me dizer que o Ensemble (o "ESB" construído sobre o Caché) estava com problemas, que os Web Services estavam retornando com erro, e que a página na Internet que fazia uso deste Web Service (é uma página em PHP) estava com caracteres malucos por causa dos erros.

Estranhei, não porque o Ensemble/Caché seja tão livre de bugs assim (porque o que tem de bugs e WTFs dentro dele não é nada desprezível), mas porque eu não havia colocado nada de novo em produção, nem mexido em configuração alguma. E também tinha certeza de que mais ninguém havia alterado nada no servidor há pelo menos dois dias.

Resolvi testar o Web Service usando o soapUI (versão 1.6), um programa open-source feito em Java que é uma mão na roda para se testar Web Services padrão SOAP (e do qual eu já até falei neste outro post). Tudo pareceu OK, o Web Service estava retornando sem erro, e com os dados corretos. Fui falar com a pessoa que havia me alertado, e disse que estava tudo bem.

Logo mais, esta pessoa volta e me diz que ainda está ocorrendo erro. Desta vez resolvo abrir a página web que utiliza o Web Service, e realmente estava com erro. Pensei que talvez estivesse exibindo algum cache, então testei novamente com o soapUI, e mais uma vez, o resultado veio correto. WTF, pensei... Perguntei para a pessoa que havia me dito do erro, EXATAMENTE o que havia acontecido, como ela havia descoberto o erro. Então me disse que havia tentado invocar o Web Service utilizando o Netbeans, e como neste retornava erro, checava depois a página web, a qual também mostrava erro.

Fiquei intrigado, e como tenho o Netbeans 6 instalado aqui, resolvi fazer os mesmos passos. E não é que usando o Netbeans para testar o Web Service, dava erro mesmo? E pior, depois de usar o Netbeans, as chamadas ao Web Service, pela página web, começavam a dar erro também. Tentei então entrar no Portal de Administração do Caché/Ensemble, e até estas páginas estavam retornando erro. Nem mesmo a página de login funcionava! Só o que eu via era a página padrão de erro do CSP.


Pronto, era o que faltava, o Netbeans derrubava o Caché/Ensemble! =P

Reiniciei o Caché/Ensemble, já que aquele ali era o servidor de produção, e pedi pra que ninguém mais acessasse ele pra fazer testes, pra não prejudicar os outros sistemas em produção, que usavam o infeliz. Fui então para o servidor de desenvolvimento e testes, a fim de descobrir o que raios estava acontecendo.

Fazendo trabalho de detetive

A primeira dica para a resolução do caso foi que no Netbeans ocorria erro, mas no soapUI não. Nos testes, mesmo depois de "derrubar" o Caché/Ensemble com o Netbeans, acessando via soapUI não ocorria erro. Resolvi investigar bem a fundo, e coloquei o Ethereal pra rodar, um bom programa sniffer, open-source, pra analisar todo o tráfego que chegava no Caché/Ensemble, no nível de stream TCP.

Como a página de erro (na figura acima) mostrava um erro relativo ao CharSet, fui verificar como o soapUI e o Netbeans enviavam as suas requisições. O CharSet é enviado junto com a requisição HTTP, fazendo parte dos cabeçalhos (Headers) HTTP, e serve para identificar qual o conjunto de caracteres que está sendo usado para enviar dados.

Abaixo uma requisição que não gerava erro, vinda do soapUI:

POST /csp/ensemble/pacote.Classe.cls HTTP/1.1
Content-Type: text/xml;charset=UTF-8
SOAPAction: "http://namespace/pacote.Classe.Metodo"
User-Agent: Jakarta Commons-HttpClient/3.0.1


E uma requisição que gerava erro, vinda do Netbeans 6.0:

POST /csp/ensemble/pacote.Classe.cls HTTP/1.0
SOAPAction: "http://namespace/pacote.Classe.Metodo"
Content-Type: text/xml;charset="utf-8"
User-Agent: JAX-WS RI 2.1.2-b05-RC1


(Modifiquei as informações referentes às classes reais e endereços, por questões de privacidade; também retirei o corpo xml pelo mesmo motivo.)

Aparentemente, as duas chamadas são bem parecidas, apesar do Netbeans enviar alguns cabeçalhos HTTP a mais (que eu retirei do exemplo acima, pra não ficar poluído demais), mas que nada influem. A versão do HTTP neste caso, também não influenciava no problema.

Analisando o Content-Type, a primeira vista não parece que haja uma grande diferença entre eles. O fato do conteúdo do CharSet ser utf-8 ou UTF-8 não influi. Entretanto, na requisição gerada pelo Netbeans, o valor do CharSet é enviado entre aspas. E isso fez TODA A DIFERENÇA.

<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
<SOAP-ENV:Envelope>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Internal Server Error</faultstring>
<detail>
<error xmlns='http://tempuri.org' >
<text>ERROR #5911: Character Set "utf-8" not installed, unable to perform character set translation</text>
</error>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


Se repararmos na tela de erro, ou na mensagem SOAP-Fault acima, veremos que a mensagem é Character Set '"utf-8"' not installed.... Ou seja, ele considerava o CharSet com ASPAS!

Apesar de ser desejável o servidor aceitar o CharSet entre aspas normalmente, retirando as aspas para "entender" qual o CharSet usado, não encontrei nada que diga que isso seja uma obrigação ou mesmo uma recomendação da especificação do HTTP.

O erro propagado

O erro ocorrer na resposta da requisição "defeituosa" é o procedimento esperado. Entretanto, depois de ter chamado o Web Service e ter recebido o erro, depois de alguns momentos, ao acessar o portal de Administração do Caché/Ensemble, este também retorna erro, e logo tudo que depende de CSPs é inutilizado, pois para qualquer requisição via browser a página de erro é mostrada. Tipicamente, os browsers não enviam o cabeçalho Content-Type, o que torna a mensagem de erro bem peculiar.

Fiz um pequeno programa em Java, para poder manipular livremente todos os cabeçalhos HTTP e enviar ao servidor. Modificando o valor de CharSet para qualquer valor, este apareceria na tela, junto com a mensagem de erro.

Além disso, comecei a testar fazendo não requisições SOAP, usando o método POST do HTTP, mas usando o método GET, que é o método usado geralmente pelos navegadores para pegar páginas Web. E não para minha surpresa, colocando-se um endereço de página CSP válido, e usando o cabeçalho com o CharSet alterado, o erro também acontecia.

Por exemplo, enviando a requisição HTTP abaixo:

GET /csp/ensemble/pacote.Classe.cls HTTP/1.0
Content-Type: text/xml;charset=Uma Mensagem Qualquer Aqui


Depois de alguns instantes, qualquer chamada a qualquer página CSP retornaria a seguinte tela de erro:


Aparentemente, o Caché mantém algumas estruturas/objetos que ele reutiliza entre as chamadas, o que causa a propagação do erro. Entretanto, se o servidor for altamente requisitado, no início o Caché já mantém vários objetos instanciados, e a chamada defeituosa vai afetar apenas aquele objeto que for usado na chamada, deixando os outros normais. Por isso, num servidor muito acessado, que mantém vários objetos instanciados, o erro fica intermitente entre as requisições HTTP posteriores, ora retornando com problema (porque usou nesta chamada o objeto com erro), ora retornando OK (porque usou outro objeto já instanciado, que não fora afetado).

Pude constatar isso ao fazer o 'ataque' na página de documentação do Caché 2007, no site da Intersystems. Ali, depois de fazer uma requisição HTTP defeituosa com o programinha que desenvolvi, algumas requisições via browser retornavam erro, enquanto outras retornavam OK, independente do endereço requisitado.

O bug provavelmente se encontra no objeto que representa uma requisição CSP, o %CSP.Request. Notem que este objeto é reutilizado, como podemos ver pela existência do método Reset, onde provavelmente reside o erro.

Explorando o bug

Alguém mal intencionado pode explorar o bug, realizando um ataque de negação de serviço em um site que use CSP. Para tal, nem é preciso realizar um imenso número de requisições, basta umas poucas requisições de tempos em tempos. Se o site tiver pouco movimento, uma ou duas requisições a cada 10 minutos seria suficiente para torná-lo inoperável.

Durante os testes, fiz uma pequena classe em Java para facilitar os testes, abrindo um Socket e escrevendo direto na stream TCP, ou seja, enviando os comandos e cabeçalhos HTTP como eu quisesse. Este programa acaba sendo uma Prova de Conceito ou do inglês, Proof of Concept.

Abaixo, listo a classe, que é bem simples (e com comentários dentro do código):


package testes;

import java.io.*;
import java.net.*;

/**
 @author Emilio, o ráqui.
 */
public class Main {

   /**
    * Uso: java testes.Main
    */
   public static void main(String[] argsthrows Exception {
       try {
           //Criação e abertura de um Socket. 
           //O primeiro parâmetro é um endereço ou IP, no exemplo, o host da
           //Intersystems que contém a documentação do Caché em CSP. Pode ser
           //alterado para qualquer endereço.
           //O segundo parâmetro é a porta TCP, como estamos lidando com HTTP,
           //o padrão é a porta 80. A não ser que o site use outra porta não-
           //padrão, esse parâmetro não precisa ser alterado.
           Socket s = new Socket("docs.intersystems.com"80);

           //Monta uma String com o comando e os cabeçalhos HTTP 
           //Primeiro, colocamos o comando GET, apontando para uma página CSP
           //válida, no caso, a página gerada com a documentação do Caché
           String cabecalho = "GET /cache20071/csp/docbook/DocBook.UI.Page.cls HTTP/1.0 \r\n"+
           //Este cabeçalho, Content-Type, que pode ser alterado para causar 
           //o bug. Basta alterar o charset para um valor inválido (ou uma
           "Content-Type: text/xml;charset=Uma Mensagem Qualquer Aqui\r\n"+
           //outros cabeçalhos HTTP podem ser colocados.
           "Connection: keep-alive \r\n"+"\r\n";

           //Cria e abre objetos para escrever e ler na stream de dados TCP
           DataOutputStream dos = new DataOutputStream(s.getOutputStream());
           DataInputStream dis = new DataInputStream(s.getInputStream());

           //envia a requisição previamente montada
           dos.writeBytes(cabecalho);

           System.out.println("Enviado");
           
           //Código para ver na saída padrão o que foi retornado pelo servidor.
           byte[] buff = new byte[32000];
           dis.read(buff);
           System.out.println("Recebido:\n" new String(buff));
           buff = new byte[32000];
           dis.read(buff);
           System.out.println(new String(buff));

       }
       catch(Exception e) {
           e.printStackTrace();
       }
   }
}


Providências

Se você roda aplicações CSP, especialmente sites na Internet, e que rodem em versões do Caché afetadas (anteriores a 2008), é altamente aconselhável que procurem a Intersystems para obter um patch.

Como aqui na empresa este patch já está instalado, e aparentemente o erro foi corrigido, o processo de obtenção do patch deve ser razoavelmente rápido (ao contrário da maioria das requisições de suporte que tivemos até agora), já que ele já foi feito.

Agora, se você preferir dar uma de 'hacker', pode fuçar e mexer no %CSP.Request. Claro que para isso, você deve tirar a base de dados CACHELIB do modo somente leitura, que é o padrão. Nesta classe, no método CSPGatewayReset (que é um método gerador de código), você pode incluir comandos para "zerar" o CharSet. Em alguns testes, essa solução também funcionou, mas como não me aprofundei mais no assunto, não aconselho a fazerem isso em um ambiente de produção.

segunda-feira, 5 de maio de 2008

Hackeando o Caché

Bem, o título chamativo acima não é verdadeiro, pois não se trata de um hack no sentido original da palavra, nem no sentido de invasão de sistemas. Se trata mais de um ataque (isso sim) de Negação de Serviço, ou como é conhecido em inglês, DoS (Denial of Service), que pode comprometer sistemas desenvolvidos usando a tecnologia CSP (Caché Server Pages) do Caché da Intersystems.

O bug que torna possível o ataque afeta as versões anteriores a versão 2008 - nos meus testes pessoais, com máquinas que eu tinha controle, testei no Caché 5.0 e 5.2. No próprio site da Intersystems, a documentação online (que é gerada em CSP) da versão 2007 também é afetada por este bug.

Como pode acontecer o ataque?

Um bug faz com que, depois de uma requisição HTTP a um endereço legítimo que use CSP (e isso inclui chamadas a Web Services do Caché), as páginas CSP chamadas depois, gradualmente comecem a retornar erro. Este erro persiste por algum tempo, até que depois de alguns minutos, que nos meus testes pareceu aleatório (mas que ficou em torno de 30 minutos), as páginas CSP retornam à normalidade.

Um hacker 'do mal' pode aproveitar este bug para tornar um site construído em CSP inoperável, simplesmente, fazendo com que de tempos em tempos, uma requisição HTTP especialmente preparada, seja enviada. Ou seja, você pode causar um ataque de Negação de Serviço (DoS) enviando uns poucos pacotes a um servidor, de tempos em tempos.

Este bug já foi reportado a Intersystems, por meio da parceira regional, e apesar de não haver encontrado nenhuma menção a este bug (nem na documentação nem na web, via google), parece que o pessoal já sabia dele, pois a versão 2008 do Caché aparentemente não apresenta este problema.

Que providências tomar?

Se você identificar que seu sistema pode ser afetado, você tem duas alternativas: ou migrar para a versão 2008, ou solicitar um "patch" para a Intersystems, para a sua versão de Caché.

Apesar de eu ser um entusiasta dos upgrades, nem tudo são flores: uma migração de versão, especialmente se for uma versão mais antiga, pode se tornar muito trabalhosa, inserir novos bugs, quebrar funcionalidades, etc... Se for este o caso, sugiro que peça emergencialmente um patch para a sua versão do Caché.

Detalhes Técnicos

Por enquanto, como ainda não obtive resposta da Intersytems, não vou liberar todos os detalhes técnicos do bug aqui no blog, nem o código que eu usei pra explorar o bug (a chamada prova de conceito). Esperarei mais algum tempo, e se o suporte (que conosco, não tem sido dos melhores) não responder satisfatoriamente, divulgarei assim mesmo o código.

sexta-feira, 25 de abril de 2008

Sincronizando Business Objects e Business Processes no Ensemble

O cenário: temos algumas informações que devem ser replicadas em diferentes bases de dados. Esta escolha de se replicar os dados, se deve a diversos fatores, que não convém mencionar aqui.

No Ensemble da Intersystems, temos um serviço de atualização de dados em duas bases de dados distintas, inclusive com estruturas diferentes. A primeira das bases de dados, em Oracle, é acessada diretamente via ODBC, pelo Ensemble. A segunda, também em Oracle, não permite o acesso direto (pois como é usada por um software de terceiros, perderia-se a garantia de performance caso o acesso fosse liberado). Para tanto, a empresa que desenvolveu este software criou uma camada utilizando Web Services (WS) padrão SOAP, para a atualização dos dados.

Além de ter uma estrutura diferente, a segunda base de dados possui algumas restrições (constraints), o que faz com que alguns dados possam ser válidos na primeira base, mas não na segunda. Essas restrições se devem a interações que alguns dados possuem dentro do segundo banco, especialmente em se tratando da codificação das entidades dentro dele (isto é, do formato/geração das chaves). Isto ocasiona que para alguns dados, a segunda base rejeita os dados, mas a primeira base os aceita.

Pois então, com estes requisitos em mente, construímos um Business Process (BP) no Ensemble, mostrado na figura abaixo simplificadamente (exclui alguns passos do processo que são irrelevantes para o entendimento deste post):



Notem que a primeira chamada é para um Business Object (BO) que lida com a chamada para o WS. O Studio, a IDE integrada do Caché/Ensemble tem um wizard para a criação de toda a estrutura de invocação de um Web Service SOAP, bastando ter o WSDL correspondente. Além de montar as classes de dados e a classe cliente do Web Service, no Ensemble você pode gerar já a estrutura de BO e mensagens de Request/Response.

O Web Service retorna como resposta uma String. Se a atualização se deu sem erros, é retornado "OK". Caso contrário, é retornado uma breve descrição do erro. Bem, o segundo elemento do BP é justamente uma checagem para ver se aconteceu algum erro. Se aconteceu algum erro, retorna imediatamente. Caso contrário, faz a chamada para dois BOs que cuidarão da atualização na primeira base, via ODBC. Note como as estruturas são diferentes: uma única estrutura de dados da segunda base (atualizada via WS) equivale a duas estruturas na primeira base (atualizada via SQL-ODBC).

Rodando diversos testes, com usuários inclusive, surgiu um problema. De vez em quando, mais frequentemente do que eu gostaria, por alguma razão, o Adapter SQL perde a conexão e/ou sessão com o Oracle, gerando exceções. Falarei mais sobre isso em um outro post, mas fica registrado que esses problemas podem ser tão graves, que necessitam até de shutdown de todo o Caché, para o Caché/Ensemble se recuperar do erro.

Acontecendo isso, você, astuto leitor, deve ter percebido que o processo no BP tem um grave problema: ele pode atualizar a segunda base via WS, e não atualizar a primeira, causando inconsistências (lembrando que o WS é simples, não tem nada de WS-Transactions, por exemplo). A solução é colocar a chamada do Web Service dentro do contexto da transação da primeira base, e se o WS falhar, executar um rollback nos SQLs já executados. Mas... como fazer isso?

Uma solução é jogar todo o código dentro de um mesmo BO (que já iria ficar enorme, já que pra colocar tudo numa mesma transação, as duas BOs que usam ODBC-SQL se tornariam uma só). Então, pra evitar mais código-macarrão dentro de um único BO, resolvi aproveitar a estrutura do BO que faz a chamada do Web Service, e manter Business Objects separados para atualizar via WS e via ODBC.

Então, eu deveria: executar as atualizações no banco via SQL, mas não dar Commit; chamar o Web Service, e caso de sucesso na atualização do segundo banco, executar o Commit, senão dar um Rollback.

Como fazer isso, usando dois BOs separados, sendo que ao terminar a execução de uma chamada, o BO com o Adapter SQL pode não necessariamente manter a sessão (pode ser até outra instância do BO a ser invocado)? A solução é deixar tudo dentro de uma única chamada do BO. Então, surge a necessidade de comunicar/sincronizar o BO com o BP, sem terminar a execução do BO. Para isso, decidi fazer uso de globais.

Simplificadamente, a solução adotada foi:

- No BP, fazer um "fork", ou seja, abrir dois caminhos de execução paralelos.
- No primeiro caminho, colocar o BO com o Adaptador SQL.

  • Neste BO, setar controle de Commit como manual.
  • Executar os comandos SQL.
  • Se ocorrer algum erro, pode sair do BP e retornar o erro.
  • Se não ocorreu erro, sinaliza usando a global, e espera pelo sinal de retorno do WS (também sinalizado via global).
  • Se o WS retornou "OK", executa o Commit.

- No segundo caminho:
  • Coloca um código de espera, até o BO com o Adaptador SQL sinalizar.
  • Fazer a chamada do BO que invoca o Web Service.
  • Sinaliza para o BO com o SQL Adapter, o resultado da invocação do WS.


Todas as sinalizações foram feitas usando uma global. Como podemos ter mais de uma instância, usamos como controle uma chave de sessão simples, para acessar a global, e evitar conflitos.

Toda esta mega-gambiarra pode ser vista no BP abaixo:



A primeira parte do código responsável pela sincronização é mostrada abaixo. Nele, que está na BP como "Seta chave sessão para global", configuramos uma chave que identificará a "sessão", usando um timestamp ($h ou $horolog, que retorna o dia/hora atual), mais um número aleatório. Usando esta chave, acessamos uma global que será a área de dados compartilhados entre a BO e a BP.


set context.chaveGlobal = $h _ $random($piece($h,",",1))
set ^globalSync(context.chaveGlobal) = 0


Ainda na BP, colocamos na parte de código "Espera por Oracle", o código abaixo, que é basicamente um loop, que de tempos em tempos, checa se o valor na global mudou. Note que o comando HANG faz com que a execução do código seja interrompida por algum tempo, mais especificamente o tempo passado como parâmetro do comando, em segundos. No código abaixo, 200 ms, ou seja, o código checa se houve mudança na variável global de 200 em 200 milisegundos.


while (^globalSync(context.chaveGlobal) < 1) {
hang 0.2
}


Dentro da BO, depois de realizar os comandos SQL, mas antes de efetuar o COMMIT, usa-se o código abaixo. Nele, a variável global muda de valor, sinalizando para o código na BP que ela pode continuar a execução. Além disso, também temos um loop de espera, pois o COMMIT só pode ser realizado depois que o Web Service retornar. Note que enquanto no código da BP a chave global está dentro de context, na BO ela está dentro da mensagem pRequest enviada à BO.


set ^globalSync(pRequest.chaveGlobal) = 1

while (^globalSync(pRequest.chaveGlobal) = 1) {
hang .2
}


Voltando a BP, depois que a BO que invoca o Web Service retorna, verificamos se este retornou OK ou se aconteceu algum erro. Em ambos os casos, sinalizamos o resultado na variável global, como mostra o código abaixo:


if (context.statusWS.StringValue = "OK") {
set ^globalSync(context.chaveGlobal) = 2
}
else {
set ^globalSync(context.chaveGlobal) = 3
}


Por fim, nos últimos passos da BO, depois de passar pelo loop de espera, temos certeza de que o Web Service retornou (ou ocorreu um erro de timeout, por exemplo). Só resta então checar o status na variável global, e realizar o COMMIT ou ROLLBACK, como mostra (simplificadamente) o código abaixo:


if (^globalSync(pRequest.chaveGlobal) = 2) {
set st = ..Adapter.Commit()
}
else {
do ..Adapter.Rollback()
}

terça-feira, 25 de março de 2008

Um dos segredos do Google: Cerveja, churrascos e muitos sushis - tudo de graça!

O ex-chef no Google, Charlie Ayes, fora contratado pelo Google antes da companhia se tornar se tornar o monstro que é hoje.

No seu livro, In Eat Yourself Smart, o chef relata as suas experiências ao ser contratado pelos dois fundadores do Google, Sergey Brin e Larry Page, e como a política do Google de recursos humanos, passando pela comida, tornou a empresa o que ela é.

Para atrair as mentes brilhantes que são o insumo da empresa, comida e bebida grátis (sempre!), e de qualidade. Inclusive cerveja e churrasco, yeah!


O chef ainda diz que um dos motores do crescimento do google foram as altas doses de sushi (!!), pois a gordura presente no peixe ajudaria as células do cérebro.

A matéria completa, do The Sunday Morning Herald (em inglês, claro), você encontra aí no link.

Via TechCrunch.

Não dá uma inveja do povo que trabalha lá?

quarta-feira, 5 de março de 2008

Coisas esquecidas pelo caminho

Tentando descobrir uma solução para alguns problemas no Caché/Ensemble da Intersystems, me deparo com o seguinte pedaço de código:

i ..sqlcode'=-0 q


Para os não-iniciados em Caché Object Script (a linguagem do Caché):

i - abreviação do comando if!!! Oh yeah baby, em Caché ou você escreve if ou abrevia para um singelo i. Só isso já mereceria um WTF pra ele.

.. - indica que o elemento a seguir pertence a classe. Em Java seria this, em Pascal, self

sqlcode - apenas o identificador de uma variável local

'= - operador de diferença. Em Java seria !=, em Pascal, <>

q - abreviação do comando quit. O comando quit, ou q, como queiram, serve para: sair de um loop ou sair de um método. Em Java seria um break ou um return, respectivamente.

Agora o que não tem explicação é o "-0".

Por que alguém compara alguma coisa com menos zero, e não com zero? Tá certo que o Caché não é muito fã de matemática (vide a precedência de operadores), mas ele sabe que menos zero e zero são a mesma coisa. Ou será que não, para nossos intrépidos colegas da Intersystems?

Eu acho que alguém acabou esquecendo o sinal lá...

quinta-feira, 28 de fevereiro de 2008

Nova propaganda para o Caché

Olhem a imagem da propaganda do Caché...


E você, meu caro desenvolvedor, cansado das tabajarices que encontra no Caché? Cansou de ouvir o pessoal de marketing dizendo 'bla bla bla', e você encontrar só bug? Não, não é bug. É feature.

Pois esta é a imagem que reflete melhor o que eu tenho encontrado:



Imagem via UsuárioCompulsivo, neste post: Não é bug... É recurso.