Página 6 de 10

Concorrência e objetos thread-safe

1a

Certamente você já concorreu com outras pessoas em sua vida. Uma vaga na faculdade, um emprego ou mesmo a fila do banco.

Sistemas de software também enfrentam certas restrições e competem por recursos computacionais. Na verdade, cada vez que você acessa um site está concorrendo, de alguma forma, com centenas ou milhares de pessoas. Um dos maiores desafios das empresas que disponibilizam serviços na Internet é atender a todos os acessos simultâneos.

Como um engenheiro de software, pretendo agora fazer uma breve análise microscópica sobre esse problema.

O que pode dar errado?

Em geral, um sistema que visa atender vários usuários ao mesmo tempo irá processar as solicitações paralelamente em vários processos ou threads. O problema começa quando as ações de dois ou mais usuários afetam o mesmo conjunto de dados.

Vamos supor que temos um objeto com um contador compartilhado entre diversas threads:

int contador = 0;
public int incrementa() {
    contador++;
    return contador;
}

Suponha ainda que duas threads T1 e T2 estão rodando em dois processadores de uma mesma CPU. Em um dado momento elas chamam, ao mesmo tempo, o método incrementa() e ocorre a seguinte sequência de execução:

  1. Processo T1 executa a linha contador++ (total = 1)
  2. Processo T2 executa a linha contador++ (total = 2)
  3. Processo T2 retorna o valor de total, ou seja, 2
  4. Processo T1 retorna o valor de total, ou seja, 2

Obviamente isso irá causar um efeito colateral indesejado! 🙁

Note ainda que problemas podem ocorrer mesmo que apenas uma das threads faça modificações e as demais estejam lendo valores, já que elas podem ler dados desatualizados e incorrer em exceções.

Um exemplo básico seria a iteração sobre uma lista. Se uma thread remove um item da lista, outras threads que estão no meio de uma iteração podem acabar em uma exceção ArrayIndexOutOfBoundsException.

Os exemplos são inesgotáveis e os efeitos colaterais os mais bizarros!

Objetos thread-safe

Para resolvermos a situação acima, onde um objeto é acessado por múltiplas threads, precisamos construir um objeto que seja à prova de acesso simultâneo, ou seja, um objeto thread-safe. O que diacho é isso? É um objeto que, em dado contexto de uso, garante o acesso seguro a dados compartilhados por várias threads de forma concorrente sem efeitos colaterais indesejados.

Bem, na verdade, essa é metade da história. O ideal do ponto de vista de desempenho seria que não houvessem acessos concorrentes. Isso significa que, num mundo perfeito, nós conseguiríamos distribuir as tarefas igualmente entre as várias threads e cada uma poderia concluir seu trabalho independentemente, sem uso de recursos compartilhados.

Na prática, é comum precisarmos compartilhar dados, então temos que aplicar um conceito muito popular da vida real: o semáforo.

Em nossas ruas, um semáforo é o que garante que somente os veículos de uma via por vez tenham o direito de atravessar um cruzamento. Em computação, um semáforo nada mais é do que uma variável ou objeto que controla o acesso a um determinado recurso. Com isso, estamos fazendo sincronização do acesso de várias threads, analogamente à sincronização dos vários semáforos de um cruzamento.

Em Java, podemos sincronizar o acesso a um método ou bloco de código. Veja o exemplo:

int contador = 0;
public synchronized int incrementa() {
    contador++;
    return contador;
}

Neste novo exemplo, as duas threads não iriam executar o método incrementa() ao mesmo tempo e no mesmo objeto. Somente uma de cada vez adentraria o bloco com o incremento, evitando o efeito colateral descrito no tópico anterior.

Não se engane! (Ressalvas importantes)

É importante notar que a sincronização no método é equivalente a um bloco synchronized(this), o que significa que o bloqueio é feito na instância do objeto que contém o método. Se houverem duas instâncias, não haverá sincronização.

Um erro muito comum é o desenvolvedor achar que o synchronized irá resolver os problemas de concorrência dos sistemas. Isso não é verdade.

Alguns frameworks web, por exemplo, podem criar várias instâncias das classes para tratar requisições, dependendo de seu escopo, então a sincronização não iria ocorrer como esperado. Além disso, aplicações distribuídas em clusters (dois ou mais servidores) também não seriam sincronizadas mesmo usando singletons.

Mesmo que synchronized fizesse o que alguns esperam, ainda incorreríamos em um grande problema.

Gargalos!

gargalo-e1353923696197

A sincronização resolve alguns problemas, mas pode gerar outros. Se houverem muitos métodos sincronizados, gargalos começarão a surgir no sistema.

Gargalos são pontos do sistema que geram lentidão, pois várias threads precisam ficar esperando sua vez. Quanto mais sincronização fizermos, mais gargalos.

Sincronizando somente o necessário

Para resolver isso, podemos usar uma forma mais adequada de sincronização. No exemplo anterior, usamos um semáforo “global”, isto é, o próprio objeto. Isso faz com que todo acesso ao objeto seja sincronizado por um único semáforo!

A solução para isso é usar semáforos mais específicos de forma que sincronizemos somente o que é necessário. Veja o exemplo:

Integer contador = 0;
Object semaforo = new Object();
public Integer incrementa() {
    synchronized (semaforo) {
        contador++;
        return contador;
    }
}

Este simples exemplo não traz muitas vantagens em si mesmo. Mas considere um cenário onde existam outros métodos nesse objeto que não modificam a mesma variável. Poderíamos criar um semáforo para cada variável, limitando ao máximo o escopo da sincronização.

A boa prática diz que o bloqueio deve ser referente aos dados acessados e não ao objeto que os contém. Isso evita muitos bloqueios desnecessários e pode fazer toda a diferença em cenários um pouco mais complexos.

E se não houver modificações?

Objetos imutáveis são, por natureza, seguros para uso em múltiplas threads, pois não há risco de efeitos colaterais. Eles não precisam ser sincronizados para serem seguramente usados em multithreading e podem ajudar muito na melhoria do desempenho da aplicação.

Este é um dos motivos pelos quais as classes básicas do Java, como String e Integer, são imutáveis. Observe que eu estou falando que não é possível modificar o valor de uma instância dessas classes, não que você não pode atribuir outra instância a uma variável.

Então nunca haverá problemas com objetos imutáveis?

Sim e não! Não é por acaso que a definição de thread-safe fala sobre o contexto de uso.

Apenas para citar um exemplo, suponha que um mapa imutável contenha elementos do tipo ArrayList mutáveis. Se threads diferentes acessarem e modificarem essas listas, estaremos sujeitos novamente a efeitos colaterais indesejados. Então, mesmo uma coleção imutável ou adequadamente sincronizada não cobre todos os casos, pois se ela contém um elemento mutável, ainda que seja um simples POJO, o resultado final não será thread-safe.

É preciso também tomar cuidado com classes do Java que, apesar de aparentarem inocência, na realidade são inerentemente thread-unsafe. Um exemplo é classe SimpleDateFormat, que estende a classe DateFormat, a qual possui um atributo do tipo Calendar, que é mutável. Aliás, ter atributos mutáveis como o Calendar é uma fonte de problemas em potencial.

Em resumo, devemos analisar todos os objetos compartilhados por threads, incluindo seu conteúdo.

Um pouquinho da API do Java

Para tentar ajudar, a API Collections do Java nos mune com várias implementações imutáveis e thread-safe para todos os gostos.

As versões sincronizadas de classes de coleções são encontradas no pacote java.util.concurrent. Eis alguns substitutos para as classes convencionais:

A classe ConcurrentHashMap é uma versão melhorada de mapa sincronizado em relação ao HashTable, suportando manipulação por várias threads, mas sem bloquear as operações de leitura.

O método utilitário Collections.synchronizedMap(map) retorna um wrapper sincronizado de um mapa qualquer. Entretanto, mesmo métodos de leitura são sincronizados, resultando em mais gargalos com relação ao ConcurrentHashMap.

Alguns dos wrappers sincronizados disponíveis são:

public static <T> Collection<T> synchronizedCollection(Collection<T> c);
public static <T> Set<T> synchronizedSet(Set<T> s);
public static <T> List<T> synchronizedList(List<T> list);
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m);
public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s);
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m);

Além disso, também existe uma lista de wrappers imutáveis, isto é, que retornam uma versão imutável do objeto:

public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c);
public static <T> Set<T> unmodifiableSet(Set<? extends T> s);
public static <T> List<T> unmodifiableList(List<? extends T> list);
public static <K,V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m);
public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<? extends T> s);
public static <K,V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> m);

A referência oficial dos métodos que retornam wrappers imutáveis e sincronizados está aqui.


Este artigo foi baseado na minha resposta no StackOverflow em Português!

Which component annotations to use in Spring 3

spring-logo

As the majority of modern frameworks, Spring 3 allows one to declare its components (Spring Beans) via annotations, making XML unnecessary. However, it can cause some confusion about the different annotations available that apparently do the same thing.

Defining where Spring should look for annotations

When we add an annotation to some class, we are putting a meta-information, that is, an arbitrary information that has nothing to do the code or the properties of the class, like its name, modifiers, etc.

However, Spring (as any other framework) needs to find and read annotated classes in order they can effectively be used.

This is done through XML configuration:

<context:component-scan 
    base-package="com.package.project.service,
                  com.package.project.controller"/>

Also through the annotation @ComponentScan:

@Configuration
@ComponentScan({
    "com.package.project.service",
    "com.package.project.controller"})
public class SpringConfiguration { ... }

In the above example, we defined in which packages Spring should search for classes. It’ll look for classes with component annotations in these packages and in their subpackages.

Grouping components with stereotypes

Firstly, there is the generic @Component. This annotation makes one class a bean available for dependency injection to the remaining components of the system.

On the other hand, there are more specific annotations that allow us to mark beans with stereotypes, as in UML. This way, the components can be grouped by their “type” or behavior.

If one class is annotated with @Service you can just suppose it contains business methods, high level transactions or some logic related to the application model.

The annotation @Repository makes it obvious that the component implements Repository design patter (which is not the same of DAO, even though they are somehow analogous).

Finally, the annotation @Controller associates a component with a MVC controller.

The benefits

At first it looks like frills, but actually there are a few advantages:

  • It helps separating the logical layers of the application.
  • It enables Aspect Oriented Programming (AOP), like Spring Data JPA module, which dynamically “implements” interfaces annotated with @Repository.
  • It allows you treat separately exceptions that are specific of a layer, using again the example of data access layer (@Repository), where Spring will translate specific database driver exceptions into standardized classes.
  • You can also create any new feature specific to a layer. Just use your imagination and creativity. 😉

Considerations

The performance of “component-scan” is my main reservation about annotations as a new standard against the traditional and verbose XMLs. If we configure a broad package, that is, containing many classes, it can slows down the application startup, since Spring will take some time to list all classes and check them for annotations. Maybe mapping your beans with XML will be better if startup time is an important requirement for you.


This article was based on my Answer on StackOverflow in Portuguese!

Quais anotações usar nos componentes do Spring 3

spring-logo

Como praticamente todos os frameworks modernos, o Spring 3 permite a declaração de componentes (Spring Beans) via anotação, tornando o uso de XML desnecessário. Porém, é comum haver confusão sobre as diferentes anotações que parecem fazer a mesma coisa.

Definindo quais classes o Spring deve considerar

Quando adicionamos uma anotação em uma classe, estamos colocando ali uma meta-informação, isto é, uma informação arbitrária que vai além do código executado e das características da classe como nome, modificador de acesso, etc.

Entretanto, o Spring (ou qualquer outro framework) precisa encontrar e ler a classe com a anotação para que ela possa efetivamente ser usada.

Isso é feito através de configuração XML:

<context:component-scan 
    base-package="br.com.pacote.projeto.service,
                  br.com.pacote.projeto.controller"/>

Ou ainda através da anotação de configuração @ComponentScan:

@Configuration
@ComponentScan({
    "br.com.pacote.projeto.service",
    "br.com.pacote.projeto.controller"})
public class ConfiguracaoSpring { ... }

Nos exemplos acima, especificamos os pacotes onde o Spring irá procurar por classes. Ele irá vasculhar qualquer classe com as anotações de componentes que estão nesses pacotes ou em subpacotes.

Agrupando componentes com estereótipos

Primeiro, existe uma anotação genérica @Component. Com essa anotação, uma classe passa a ser um bean disponível para injeção de dependências nos demais componentes do sistema.

Por outro lado, há também anotações mais específicas que possibilitam marcar as classes com estereótipos, assim como na UML. Dessa forma, os componentes podem ser agrupados por seu “tipo” ou comportamento.

Se uma classe é anotada com @Service pode-se facilmente pressupor que ela contém regras de negócio, transações ou lógica relacionada ao modelo da aplicação.

Se a anotação é @Repository, fica óbvio que a classe implementa o padrão de projeto Repository (que não o mesmo que DAO, mas é de alguma forma análogo).

Por fim, se a anotação é @Controller podemos associar a classe diretamente a um controlador do padrão MVC.

Alguns benefícios

Embora tudo isso a princípio pareça apenas um mero enfeite, é possível listar algumas vantagens:

  • Ajuda na separação lógica de camadas da aplicação.
  • Possibilita a utilização de Programação Orientada a Aspecto (AOP – Aspect Oriented Programming), como usado módulo Spring Data JPA, o qual “gera” dinamicamente a implementação de interfaces anotadas com @Repository.
  • Permite o tratamento pontual de exceções lançadas por camadas específicas, novamente com o exemplo da camada de acesso a dados (@Repository), onde o Spring irá traduzir as exceções de cada driver de banco de dados para classes padronizadas.
  • Você também pode criar qualquer funcionalidade que se aplique por camada, basta usar um pouco a imaginação. 😉

Considerações

Minha única ressalva com relação ao uso das anotações como novo padrão em detrimento dos tradicionais e “verbosos” XML’s é com relação ao desempenho da configuração component-scan. Se definirmos um pacote muito abrangente, isto é, que contém muitas classes, o Spring irá levar um tempo considerável listando as classes a procura das anotações. Mapear os beans em XML ajuda se o tempo de inicialização da aplicação for um requisito importante.


Este artigo foi baseado na minha resposta no StackOverflow em Português!

HTML e Javascript: trocando dois elementos de posição (swap)

Você tem dois elementos quaisquer na estrutura do documento da sua página (DOM). Como invertê-los, isto é, colocar um no lugar do outro?

Abordagens para solução

O problema não é tão simples quanto pode parecer a princípio, pois ao movermos um dos elementos, perdemos sua posição original e não é tão fácil armazenar a localização de um elemento em Javascript.

Vamos analisar algumas abordagens nos tópicos abaixo.

Variável auxiliar

Podemos usar o princípio básico da troca de valores entre variáveis. Por exemplo, dadas as variáveis A e B, podemos trocar os valores usando uma variável auxiliar AUX:

AUX = A
A = B
B = AUX

Simples, não? Mas estamos aqui falando da posição de elementos na estrutura HTML. O que seria nossa variável auxiliar? A resposta é: um elemento criado dinamicamente.

Colocando isso numa solução reutilizável em forma de um plugin para jQuery:

(function ($) {
    $.fn.swap = function(anotherElement) {
        var a = $(this).get(0);
        var b = $(anotherElement).get(0);
        var swap = document.createElement('span');
        a.parentNode.insertBefore(swap, a);
        b.parentNode.insertBefore(a, b);
        swap.parentNode.insertBefore(b, swap);
        swap.remove();
    }
}(jQuery));

Demo no jsfiddle

A utilização é muito simples:

$(seletorElemento).swap(seletorOutroElemento);

Puff! Os elementos foram trocados.

Armazenando a posição do elemento

Outra abordagem, que não envolve a criação de um elemento adicional, é armazenar a posição de pelo menos um dos elementos. Na verdade, só precisamos armazenar a posição do elemento que será movido primeiro.

O problema está em como representar a posição de um elemento, pois a sua inserção será feita relativamente a algum outro. Se usarmos o anterior como referência, teremos problemas se ele for o primeiro. Se usarmos o posterior, teremos problemas se ele for o último. Pior, os dois elementos a serem trocados podem estar próximos um do outro, em sequência!

Não vou descrever minuciosamente as nuances do algoritmo que desenvolvi porque não creio ser interessante, mas considere a seguinte implementação:

(function ($) {

    $.fn.swap = function(anotherElement) {

        var sameParentStrategy = function(one, another) {
            var oneIndex = one.index();
            var anotherIndex = another.index();
            var swapFunction = function(first, second, firstIndex, secondIndex) {
                if (firstIndex == secondIndex - 1) {
                    first.insertAfter(second);
                } else {
                    var secondPrevious = second.prev();
                    second.insertAfter(first);
                    first.insertAfter(secondPrevious);
                }
            }
            if (oneIndex < anotherIndex) {
                swapFunction(one, another, oneIndex, anotherIndex);
            } else {
                swapFunction(another, one, anotherIndex, oneIndex);
            }
        };

        var differentParentsStrategy = function(one, another) {
            var positionStrategy = function(e) {
                var previous = e.prev();
                var next = e.next();
                var parent = e.parent();
                if (previous.length > 0) {
                    return function(e) {
                        e.insertAfter(previous);
                    };
                } else if (next.length > 0) {
                    return function(e) {
                        e.insertBefore(next);
                    };
                } else {
                    return function(e) {
                        parent.append(e);
                    };
                }
            }
            var oneStrategy = positionStrategy(one);
            var anotherStrategy = positionStrategy(another);
            oneStrategy(another);
            anotherStrategy(one);
            return this;
        };

        //check better strategy
        var one = $(this);
        var another = $(anotherElement);
        if (one.parent().get(0) == another.parent().get(0)) {
            sameParentStrategy(one, another);
        } else {
            differentParentsStrategy(one, another);
        }

    };

}(jQuery));

Note as diferentes estratégias de troca, dependendo dos elementos serem ou não filhos de um mesmo “pai”.

Demo no jsfiddle.

Clonagem dos elementos

Uma terceira alternativa, um tanto simplista na verdade, é clonar os dois elementos e substituir os originais um pelo outro. Vejamos o código:

(function ($) {
    $.fn.swap = function(anotherElement) {
        // cache elements
        var $div1 = $(this),
            $div2 = $(anotherElement);
        // clone elements and their contents
        var $div1Clone = $div1.clone(),
            $div2Clone = $div2.clone();
        // switch places
        $div1.replaceWith($div2Clone);
        $div2.replaceWith($div1Clone);
    }
}(jQuery));

Análise das soluções

Cada abordagem parece ter seu ponto fraco. Criar um elemento auxiliar deixa qualquer um com um pé atrás por causa da criação e inserção de um elemento “inútil” ao DOM. Porém, a complexidade do algoritmo que calcula a posição dos elementos e as várias funções usadas da API do jQuery chega a dar calafrios na espinha. E quanto a clonar os elementos? Isso não pode ser algo do bem!

Note que a primeira abordagem, por ser simples e direta, pode facilmente ser implementada sem usar a API do jQuery. Usei propositalmente a API nativa do Javascript para gerar um ganho de desempenho, pois, como veremos a seguir, isso faz muita diferença. Já as demais abordagens exigiriam alterações nada triviais para depender apenas da API nativa.

Ao invés conjecturar, vamos logo ao teste de desempenho. Preparei oito cenários com os algoritmos de troca descritos acima. Para cada algoritmo, o desempenho para troca de nós que estão no mesmo nível e o desempenho para troca de nós que estão em locais diferentes do HTML foram medidos separadamente. Além disso, o algoritmo que usa a “variável auxiliar” foi desmembrado em dois: o primeiro usando a API do jQuery e o segundo, como descrito no respectivo tópico, usando a API nativa.

Considere o seguinte gráfico:

Desempenho de algoritmos de swap

A legenda para relacionar cor e algoritmo:

  • Azul escuro: nós no mesmo nível armazenando a posição do elemento.
  • Vermelho: nós em níveis diferentes armazenando a posição do elemento.
  • Amarelo: nós no mesmo nível com variável auxiliar e a API do jQuery.
  • Verde escuro: nós em níveis diferentes com variável auxiliar e a API do jQuery.
  • Roxo: nós no mesmo nível com variável auxiliar e API nativa, sem jQuery.
  • Azul claro: nós em níveis diferentes com variável auxiliar e API nativa, sem jQuery.
  • Pink: nós no mesmo nível usando clonagem.
  • Verde claro: nós de níveis diferentes usando clonagem.

Teste no jsperf

Conclusões

Os resultados confirmaram algumas das suspeitas:

  • A clonagem é muito lenta.
  • Usando a API do jQuery, armazenar o local da variável é mais rápido do que criar e inserir um elemento auxiliar.
  • Porém, usar a API nativa do Javascript gerou um ganho em torno de 10x no desempenho. Mesmo com a alteração no DOM gerada pera inserção da variável auxiliar, o desempenho desta solução superou todas as demais.

Não pretendo aqui tirar nenhum mérito de uma excelente biblioteca como o jQuery, pois em muitos casos ela nos permite obter um desempenho muito superior a uma “solução caseira”. Porém, não devemos tomar qualquer biblioteca por uma solução definitiva para todos os problemas.

Muitos cenários exigirão código específico e usar uma API nativa, de modo a remover camadas de abstração, muitas vezes é o melhor caminho.

Nós, desenvolvedores, devemos ser flexíveis o suficiente para trabalhar em diferentes níveis de abstração. Devemos saber quando “baixar” o nível… 😉

Este artigo foi baseado na minha resposta no StackOverflow em Português e nas respostas de outros excelentes desenvolvedores como Marcelo Gibson e Zuul!

Results of a social experiment

ExperimentingGeneWilder

After a few years working on IT, always learning new things, I think I came to a point where I have a reasonable experience. However, I noticed that talent and effort aren’t equivalent to professional growth and recognition.

My influence as professional were only over my small circle of colleagues, inside my company. For the rest of the world, I was just nobody.

No, it’s not about fame I’m talking about, but in showing who really I am. If someone put my name on Google today, what he/she will see? An unknown? Or a professional respected by what he does, knows and is?

At the beginning of 2013 I decided to start a personal and social experiment, taking the heat for exposing myself.

The scenario

In the first 5 years working with IT there weren’t one day when I haven’t studied or created something different, even if it was one simple pattern applied to one simple class. But even then I often felt kind of stagnated.

On the other hand, sometimes there was a “wave of maturity”, when I felt a sort of adrenaline rush realizing I was improving a lot and fast. But, as the time passes, the growth stops and soon the stagnation shows up again. In order to start it again it was necessary to reinvent myself time after time.

Despite of it, though I was distinguished inside my small circle, I was nobody for the IT community. There wasn’t anyone outside my company who knew my reputation, nothing online that proved something about me.

How to overcome that?

Analyzing previous improvements

One of the major “waves of maturity” occurred to me while I was writing a paper about software estimation at my post graduation (specialization) course. My advisor professor was Dr. Ítalo Vega. In each analysis of my text, he explained concepts far beyond what I had learned in college or reading books. I learned so much about how critical thinking works and about the difference between objective versus subjective arguments. I can say my entire worldview was affected, the way I thought about facts, evidences, and opinions.

What happened there? What started that evolution? I could comprehend the following main points:

  • Exposition of my work to analysis and critic of a good professional.
  • Work together with someone who knows (a lot) more than I.

How could I reproduce that experience?

The first action: this blog

With my mind blowing with new ideas and a new vision of IT acquired from my specialization, my first act was to start this blog.

I had a double intent: help less experienced professionals and disseminate a few things I had learned. This way, if one wants to know who Luiz Ricardo is, he just would need to spend a bit of time reading through this page to have a good idea about my profile, experience, depth. I can say these goals was and are being achieved.

But there’s a problem with the blog (as any website): isn’t not enough to publish content online. I had to interact with good professionals, so I decided to join…

Online developer communities

Knowing about communities like GUJ (Grupo de Usuários Java, Java User Group) and StackOverflow, I bet it would be an interesting way to asset my knowledge. Could I help other professionals as I did with my colleagues? Or did my knowledge is restricted to my small workplace?

GUJ Respostas (GUJ Answers)

My first “target” was GUJ Respostas, a platform like StackOverflow, different from traditional forums. Users ask questions and others answer them. Users can vote for or against questions and answers. Clarifications can be made via comments. For each vote received, the user that asked a good question or gave a good answer earns a few reputation points. Votes against questions or answers subtract points from the user.

As I started answering questions, I had a few snags. When we deal with someone personally we can hear the problem and ask or check whatever we want. At distance, it’s necessary to pay attention to details, suppose correctly, and realize that if we write nonsense someone will be there to point this out. It’s a good training to the Chrystal Ball all good software engineers must own.

I joined GUJ Respostas in October, 2013. Three months later I reached the first position in the rank.

StackOverflow em Português (StackOverflow in Portuguese)

Some time later, on December 13, 2013, the Portuguese speaking community version of StackOverflow began. I joined since the first day of private beta!

StackOverflow is a community with goals like organizing and centralizing information about programming and related issues, though there are other interesting communities sharing the same ideal on StackExchange network. StackOverflow is more demanding about the content, moderators act boldly to maintain the level of quality.

I don’t need to say that I had some issues in the beginning. But, once more, exposing myself to other professionals I was susceptible of analysis and critics.

In the end, the results were also very positive. After three months of active participation I reached the first position in the rank.

Review of the experiment

Obviously, reaching the first place on both sites doesn’t prove at all I’m better than any other user. This isn’t also an objective qualifier of my skills.

However, at least I can say as a professional I was useful to many people, I have a fairly wide and deep knowledge, enough to help in different areas. 😉

I also won confidence before interviews and discussions about technology in general.

Furthermore, many times I was criticized. With good reason in some occasions, not so much in others. Sometimes other professionals answered better. Little by little, I compared my approach with theirs and improved my reasoning and thought process.

I don’t know for how long I’ll be in the first position on these sites. Indeed, it doesn’t matter. Perhaps I’ll spend my time in other activities to get better in different aspects. But it certainly was a great experience!

Conclusions for the reader

I believe those principles mentioned above count for every professional, namely:

  • Expose yourself to analysis and critic.
  • Learn/work with better people.

Leave your comfort zone

Maybe you are the best in your group. But, probably, you are the best only in the stuff you are better. And what about the areas where others are better than you? Probably you have something to learn, isn’t?

There’s always someone better. It’s a basic principle.

Be social

Even good professionals can get out-of-date and alienated. Make sure you have contact with different IT subcultures and increase your contact list. This can bring many benefits to your career, and I’ll list two of them:

  1. Learn how to express your thoughts better and defend your ideas. This can contribute to get a promotion.
  2. Be aware of the news in technical and market issues. This will keep you above the average and could even help you go get a better job.

Is it worth repeating my experiment?

It depends.

Participating in developer communities is a valid way to help remote colleagues and gather knowledge outside your daily environment.

However, I don’t recommend invest too much time or impose on yourself to reach a specific position in the rank.

There are other ways to improve yourself professionally. This is only one of them. Maybe, in your case, it would be better read those dusty books on your shelf, make a specialization course, or participate in open source projects, where your code will be assessed by more experienced developers.

The important thing is: always reinvent yourself and don’t stand still!

Resultados de um experimento social

ExperimentingGeneWilder

Após alguns anos trabalhando com TI, sempre me atualizando a aprendendo coisas novas, acredito ter juntado uma boa bagagem profissional. Porém, notei que talento e esforço não são sinônimos de reconhecimento e crescimento profissional.

Minha influência como profissional atingia apenas meu círculo mais próximo. Para o restante do mundo, eu era apenas um pobre desconhecido.

Não, não é de fama que estou falando e sim sobre como mostrar quem realmente você é. Se alguém colocar meu nome no Google, o que ela vê? Um nada? Um desconhecido? Ou alguém que é respeitado por aquilo que faz, sabe e é?

No início de 2013 decidi começar um experimento pessoal e social, isto é, dar minha “cara a tapa” mesmo!

Explicando melhor o cenário

Em meus primeiros 5 anos trabalhando foi raro um dia em que não estudei alguma coisa nova ou criei algo diferente, ainda que seja o simples prazer de aplicar um pattern simples numa classe simples. Mesmo assim às vezes me sentia estagnado.

Em determinados períodos, houve uma “onda de amadurecimento”, aquela adrenalina de sentir que realmente estava progredindo. Mas, com o tempo, o crescimento desacelera e logo a estagnação aparece novamente. Para continuar crescendo e amadurecendo era então necessário reinventar-se para audaciosamente ir onde nenhum nerd jamais esteve (não entendeu?).

Além disso, embora de certa forma eu tenha me destacado no meu restrito círculo profissional, eu não era “ninguém” para a comunidade de TI. Não havia nada além de colegas imediatos que pudessem dar um aval da minha reputação, nada online que provasse algo sobre mim.

Como superar isso?

Analisando progressos anteriores

Uma das maiores “ondas” de amadurecimento ocorreu no desenvolvimento da minha monografia sobre “Estimação de Software” na PUC-SP. Fui orientado por, nada mais, nada menos que o Dr. Ítalo Vega. A cada análise do meu texto, ele parava e me explicava conceitos muito além do que eu já havia aprendido em qualquer lugar. Aprendi muito sobre como funciona uma mente crítica em relação a argumentos objetivos e subjetivos. Posso dizer que toda minha visão de mundo foi afetada, meu modo de pensar e discutir sobre fatos, evidências e opiniões.

Em linhas gerais, o que aconteceu ali? O que deu início a tal evolução? Pude compreender os seguintes pontos:

  • Exposição do trabalho à análise e crítica de um bom profissional
  • Trabalho em conjunto com quem sabe (muito) mais do que eu

Como repetir essa experiência?

A primeira atitude: este blog

Com a mente borbulhando por ideias e a nova visão sobre TI adquirida na pós-graduação, minha primeira atividade foi começar este blog.

A ideia é dupla: tentar ajudar profissionais mais novos e expor algum conhecimento em forma de texto. Com isso, quem deseja saber quem é o profissional Luiz Ricardo precisa apenas gastar alguns momentos de leitura para identificar meu perfil, minha maturidade, minha profundidade. Nesses quesitos, posso dizer que o objetivo foi (e continua sendo) alcançado.

O problema com o blog (e qualquer site) é que não adianta nada apenas colocar o domínio no ar e postar algum conteúdo. A interação com outros bons profissionais não vem automaticamente. Decidi ir além, a procura de…

Comunidades de desenvolvedores

Sabendo da existência de comunidades como GUJ (Grupo de Usuários Java) e StackOverflow, apostei que seria uma forma interessante de colocar meu conhecimento à prova. Será que eu poderia ajudar aleatoriamente outros profissionais, assim como ajudava meus colegas da empresa? Ou seria meu conhecimento restrito ao meu pequeno ambiente de trabalho?

GUJ Respostas

Meu primeiro “alvo” foi o GUJ Respostas, um sistema parecido com o StackOverflow e diferente de um fórum tradicional. Nesse sistema, usuários criam perguntas e os demais publicam as respostas. Os usuários podem votar contra ou a favor nas perguntas e respostas. Esclarecimentos são feitos através de comentários. A cada voto recebido o usuário que fez uma boa pergunta ou resposta ganha certa quantidade de pontos. Votos contra debitam pontos do usuário.

Ao começar respondendo perguntas, tive alguns percalços. Quando lidamos com alguém pessoalmente podemos escutá-la relatar seu problema e perguntar o que quisermos. Tratando à distância, é preciso atentar-se aos detalhes, pressupor corretamente e saber que se falarmos besteira vai ter alguém lá para apontar isso. É um bom treino para a bola de cristal que todo bom engenheiro de software deve ter.

Fiz o cadastro no GUJ Respostas em Outubro de 2013. Três meses depois atingi a primeira colocação no ranking.

StackOverflow em Português

Algum tempo depois, em 13 de dezembro de 2013, deu-se início à comunidade de língua portuguesa do StackOverflow. Aderi desde o primeiro dia do beta privado!

Para quem não sabe, StackOverflow é uma comunidade que pretende organizar e centralizar informação sobre programação e assuntos correlatos, muito embora, haja muitas outras comunidades interessantes que compartilham do mesmo ideal no StackExchange. A comunidade do StackOverflow costuma ser bem mais exigente quanto ao conteúdo de perguntas e respostas publicadas e os moderadores agem fortemente no intuito de garantir a qualidade do site.

Bem, não sei se preciso dizer que tive alguns trancos no começo. Mais uma vez, ao me expor para outros profissionais, fui passível de análise e críticas.

No fim, os resultados também foram positivos. Após 3 meses participando ativamente atingi novamente o primeiro lugar no ranking.

Análise da experiência social

Obviamente, atingir o primeiro lugar em pontuação nos dois sites não prova de forma alguma que eu sei mais do que todos os que estão lá. Também não é, em si mesmo, um qualificador objetivo de minhas competências.

Porém, pelo menos posso dizer que sou útil como profissional para várias pessoas e que meu conhecimento é abrangente o suficiente para ajudar em muitas áreas diferentes. 😉

Ganho também em confiança diante de possíveis entrevistas e discussões sobre tecnologia em geral.

Além disso, muitas vezes fui criticado. Algumas com razão, outras não. Em determinadas ocasiões outras pessoas deram respostas melhores que as minhas. Aos poucos, comparei minha abordagem frente aos problemas com a de outras pessoas e pude aprimorar meu raciocínio, minha forma de pensar e agir.

Não sei por quanto tempo vou continuar em primeiro lugar nos sites. Na verdade, isso nem importa tanto. Talvez eu passe a dedicar esse tempo a outras atividades para continuar avançando como profissional. Mas foi uma ótima experiência!

Conclusões para o leitor

Creio que os princípios expostos acima valem também para todo profissional, a saber:

  • Expor-se à análise e crítica
  • Aprender/trabalhar com pessoas melhores

Saia da sua zona de conforto

Talvez você seja o melhor no seu grupo. Mas, provavelmente, você é melhor somente naquilo que você é melhor. E quanto às áreas onde os outros são melhores? Provavelmente você tem algo a aprender.

Além disso, sempre há alguém melhor. É um princípio básico.

Seja social

Mesmo bons profissionais ficam defasados e alienados ao se fecharem dentro de si mesmos. Tenha contato com diferentes subculturas de TI e aumente o quanto puder o seu leque de contatos. Isso pode trazer vários benefícios para sua carreira, dos quais vou citar dois:

  1. Aprender a expor melhor seus pensamentos e defender suas ideias, o que pode contribuir para promoções.
  2. Estar por dentro das novidades em termos técnicos e também sobre o mercado, o que vai mantê-lo acima da média e pode até ajudar a conseguir um bom emprego.

Vale a pena repetir meu experimento?

Depende.

Participar de comunidades é um meio válido para ajudar colegas distantes e agregar conhecimento de fora do seu ambiente cotidiano.

Entretanto, não recomendo investir tempo demais nisso, muito menos ter como objetivo uma posição específica no ranking.

Existem outras formas de se aperfeiçoar profissionalmente. Esta é apenas uma delas. Talvez, no seu caso, seja melhor tirar o pó daqueles pobres livros abandonados (ou comprar novos). Ou, quem sabe, matricular-se num curso de extensão ou especialização. Outra possibilidade é participar de projetos de código-aberto, onde seu código será avaliado por desenvolvedores mais experientes.

O importante é reinventar-se sempre e não ficar parado!

O que são e como funcionam transações em SQL?

Bancos de dados relacionais que operam no padrão SQL em geral são transacionais, isto é, eles permitem a execução de uma sequência de operações como um bloco indivisível de forma a garantir a integridade dos dados em um ambiente com acesso concorrente.

O Problema

Imagine três operações sequenciais que afetam a base de dados e não usamos uma transação para controlá-las. Vamos usar como exemplo um caso de uso comum de um e-commerce:

  1. Verificar se possui o produto em estoque
  2. Inserir um novo registro da compra
  3. Debitar o estoque

Agora vamos supor que dois clientes estão tentando finalizar suas compras neste e-commerce fictício. O servidor recebe duas requisições quase simultaneamente e começa a processar os pedidos na sequência apresentada acima. Os dois pedidos estão sendo processados paralelamente em threads diferentes. Tanto o cliente A quanto o cliente B selecionaram um produto com apenas uma unidade em estoque.

Podemos acabar com a seguinte linha de execução:

  1. Thread A verifica o estoque (passo #1) e insere o registro da compra (passo #2)
  2. thread A é bloqueada e B passa a ser executada
  3. Thread B verifica o estoque (passo #1), o qual ainda não foi debitado, e insere o registro da compra (passo #2)
  4. Thread B atualiza o estoque (passo #3), que agora fica zerado
  5. Thread B é bloqueada e A passa a ser executada
  6. Thread A atualiza o estoque (passo #3), que agora fica negativo!

Apesar de verificarmos o estoque a ordem de execução dos diferentes processos é imprevisível, então neste cenário concorrente ela não traz garantia do valor no passo seguinte.

A Solução

Bancos de dados transacionais usam o conceito ACID:

  • Atomicidade: uma transação é uma sequência de operações indivisível, ou é executado como um todo, ou tudo é desfeito.
  • Consistência: ao final da transação, o estado dos dados deve ser consistente.
  • Isolamento: embora alguns sistemas permitam quebrar o isolamento, em geral, uma transação em andamento não pode ser acessada por outras transações de modo a evitar leitura de um estado inconsistente, uma “sujeira”.
  • Durabilidade: em caso de sucesso (commit) a persistência dos dados deve ser garantida

Para garantir esses conceitos, em geral, os bancos de dados usam bloqueios quando ocorrem acessos simultâneos à mesma estrutura de dados. Ou seja, se alguém já está mexendo nos dados, os demais tem que aguardar sua vez numa fila até ele acabar.

Na prática

Ao usar bancos de dados transacionais, nós podemos usufruir deste controle de gerenciamento por parte dos SGBDRs (Sistemas Gerenciadores de Bancos de Dados Relacionais).

Incluindo o conceito de transação ACID no exemplo anterior, vamos ver como fica a execução:

  1. Thread A inicia uma transação, verifica o estoque (passo #1) e insere o registro da compra (passo #2)
  2. Thread A é bloqueada e B passa a ser executada
  3. Thread B inicia uma transação, mas ao tentar verificar o estoque ela é bloqueada porque a transação de A ainda não acabou
  4. Thread A atualiza o estoque, que agora fica zerado, e faz commit na transação.
  5. Thread B é desbloqueada e passa a ser executada
  6. Thread B conclui a verificação do estoque (passo #1) e retorna um erro pois não encontra o produto disponível no estoque
  7. Thread B executa um rollback para desfazer outras alterações que tenha efetuado, por exemplo, se houve outro produto debitado na mesma transação.

O resultado final é como se somente a thread A tivesse executado e B nunca existisse.

Nem tudo é um mar de rosas

Existem alguns problemas inerentes a transações ACID, sendo o desempenho o maior deles.

Embora seja importante garantir a integridade dos dados, para muitos sistemas onde a disponibilidade é o fator mais crítico um modelo que bloqueia acessos simultâneos torna-se inviável. Este é um dos principais fatores para o surgimento e a adoção de diversos sistemas de bancos de dados não transacionais e NoSQL.

O importante é entender que o uso de transações tem um custo e em algumas ocasiões este pode ser alto demais. Uma das representações mais comuns do trade-off de persistência de dados é a seguinte (retirada deste artigo):

tradeoff-database

O gráfico demonstra que consistência, disponibilidade e particionamento (escalar o banco de dados em diversos nós) são recursos que afetam uns aos outros. Você simplesmente não pode ter o melhor dos três e isso foi provado no teorema de CAP.

Bancos de dados relacionais geralmente sacrificam o particionamento em prol da consistência e da disponibilidade, enquanto alguns sistemas NoSQL sacrificam a consistência dos dados.

Note que o termo “geralmente” ou “em geral” foi cuidadosamente utilizado no artigo porque os diferentes sistemas de bancos de dados permitem vários níveis de configuração de isolamento de transações e acesso simultâneo, possibilitando um ajuste fino do desempenho de cada funcionalidade

Este artigo foi baseado na minha resposta no StackOverflow em Português!

Como desabilitar campos em sistemas web

Quando lidamos com sistemas web, é frequente a necessidade de desabilitarmos algum campo na página, com a finalidade de impedir a alteração por parte do usuário.

O problema é que o navegador não envia campos desabilitados com disabled na submissão do formulário. Esta característica comumente deixa os novatos atordoados.

Solução: readonly

Uma solução é usarmos o atributo readonly, porém existem efeitos colaterais:

  • Se for um campo de texto, o usuário poderá selecionar e copiar o conteúdo, o que pode ser uma vantagem em alguns casos.
  • Além disso, os estilos do campo permanecerão os originais, isto é, o usuário poderá não perceber que se trata de um campo somente-leitura.

Para contornar o segundo ponto e dar feedback visual para o usuário, adicione um estilo para manter a usabilidade, por exemplo, deixando o fundo da caixa um pouco acinzentado.

Exemplo com CSS inline:

<input type="text" value="Valor fixo" 
    style="background: #EEE; cursor: not-allowed; color: #777"
    readonly />

Exemplo com uma classe CSS:

.desabilitado {
    background: #EEE; 
    cursor: not-allowed; 
    color: #777;
}

<input type="text" value="Valor fixo 2" class="desabilitado" readonly />

Veja o exemplo funcional do código acima no jsfiddle.

Considerações de segurança

Alguém pode argumentar que desabilitar campos na tela não é uma boa prática do ponto de vista de Segurança da Informação, pois hoje um usuário pode facilmente burlar essa limitação através da Ferramenta do Desenvolvedor (F12), alterando as propriedades do campo. Isso está correto, em parte.

O importante é entender que a necessidade de desabilitar campos é parte essencial do funcionamento dos sistemas. Não é seu objetivo garantir a segurança da informação daquele campo.

Este artigo tem por objetivo auxiliar o desenvolvedor front-end com um elemento puramente visual e não isenta as validações necessárias no back-end.

Entendido!?

Este artigo foi baseado na minha resposta no StackOverflow em Português!

Running Python (or any language) in your browser

browsers-war-java-script-engines

Some time ago a very interesting question arose in StackOverflow in Portuguese about running an arbitrary programming language in the browser, just like Javascript does.

How could we run our own language in a wesob page? Let’s face the challenge and implement it!

Requirements

There are two ways to include Javascript in HTML documents:

Inside a <script> tag:

<script type="text/javascript">
alert('javascript running');
</script>

From an external source:

<script type="text/javascript" src="script.js"></script>

We want to use our language in the same fashion.

Inside a <script> tag:

<script type="text/ourlanguage">
...
</script>

From an external source:

<script type="text/ourlanguage" src="our-script.abc"></script>

We won’t consider inline code, as in onclick, onchange, and other HTML tag attributes.

Keep in mind that <script> tags have certain characteristics we want to preserve;

  • They’re executed sequentially, in the order they appear in HTML.
  • If a script has the src attribute defined, it has to be downloaded and executed before the browser continues to parse the rest of the page or execute any other script. This is the reason we should only include scripts in the end of the source (close to </body>) whenever it’s possible.

Also, our new language should coexist with Javascript code in the web page, as in the example below:

<script type="text/javascript">
var a = 6;
</script>
<script type="text/ourlanguage">
print(a) # should prints 6
</script>

Working the solution

As browsers ignore script languages they don’t recognize, there is no problem if we use the <script> tag with an arbitrary type attribute. But how can we do the browser run it?

At first, I thought the only solution would be through a generic loader in javascript, which would able to load the entire page and process its content, adding all the HTML tags programmatically to the DOM and running scripts as they would be found.

It’s nothing more, nothing less, than a Javascript version of PHP or some template processor, isn’t it? It looks to complicated to be useful.

However, what I found out is that modern browsers provide an API to intercept web page changes, including those when new tags are added to the DOM during the HTML parsing. We’re talking about MutationObserver.

The possibilities are extraordinary!

Using MutationObserver

To use MutationObserver we just need to instantiate it passing a listener function to the constructor. The function will receive changes in the page as an argument. Then, we just call observe method in the instance passing the element we want to monitor. In this case, it’s the entire document.

Let’s see a basic example:

// creates the observer instance
var mutationObserver = new MutationObserver(function(mutations) {

    // iterates over the changes in the document
    mutations.forEach(function(mutation) {

        // gets all added nodes
        var addedNodes = mutation.addedNodes;

    });

});

// starts to monitor for changes in any level of the document
mutationObserver.observe(document, { childList: true, subtree: true })

Now we can check if there’s added notes of our language and then delegate the execution to its interpreter.

Choosing a language: Python

Which language to use? It should be a high-level, powerful, productive language when compared to Javascript.

Python looks like a great choice!

Building (or borrowing) an interpreter

The first step would be to build an interpreter in Javascript for our language. However, we won’t reinvent the wheel! There is implementations of the language for various platforms already. In Javascript we have a Python 3 implementation called Brython.

This project accomplishes partially our requirements about execution in <script> tags and acessing Javascript objects and variables. But Python scripts run only after the webpage is fully loaded in the the onload event from body. We want it like movie credits, that is, in the order of appearance of <script> tags, remember?

Look at the original Brython use case:

<body onload="brython({debug:0, cache:'none'})">

But, we can use MutationObserver to delegate Python scripts to Brython as they are found in the web page, offering a better integration and meeting our requirements.

Delegating the execution to Brython

Our language depends on Brython interpreter, so we need to know how to delegate scripts to it.

The bad news is that Brython don’t provide a public API to do that. The good news is that we’re dealing with an open-source project, so we can inspect the source to find out how he does its magic!

All we need is in the following excerpt:

//defines the module name as 'main' because it's in the main page
**BRYTHON**.$py_module_path['**main**'] = window.location.href;

//runs the analyzer/converter to Javascript 
var $root = **BRYTHON**.py2js($python_source, '**main**');

//gets the actual Javascript code
$javascript = $root.to_js();

Choosing the name for our scripting language: Pyscript

Our scripting language deserves a good name. As luizscript is too ugly, I decided to calculate:

Javascript – Java + Python = Pyscript

So, the tag will be like this:

<script type="text/pyscript"></script>

Adding support to Pyscript language

With all we have learned until now, let’s implement a solution using MutationObserver to add support to Pyscript in a web page.

This is the main implementation in the file pyscript.js:

//inits brython 
brython();

//creates observer instance
var mutationObserver = new MutationObserver(function(mutations) {

    //iterates over document changes
    mutations.forEach(function(mutation) {

        //gets new node
        var node = mutation.addedNodes[0];

        //is it the type we want?
        if(node && node.tagName === 'SCRIPT' && node.type === 'text/pyscript') {

            //test log
            console.log('Pyscript found!');

            //python source
            var $src;

            //If src attribute is found, do a synchronous ajax to get 
            //the code in order to execute it immediately
            if (node.src!=='') {

                if (window.XMLHttpRequest){
                    // for IE7+, Firefox, Chrome, Opera, Safari
                    var $xmlhttp = new XMLHttpRequest();
                } else {
                    // for IE6, IE5
                    var $xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                }
                $xmlhttp.open('GET', node.src, false);
                $xmlhttp.send();
                if ($xmlhttp.readyState === 4 && $xmlhttp.status === 200) {
                    $src = $xmlhttp.responseText;
                }
                if ($src === undefined) { // houston, we have a problem!!!
                    console.log('Error loading pyscript: ' + node.src);
                    return;
                }

            } else {

                //without src, source is tag content
                $src = node.textContent || node.innerText;

            }

            //python -> javascript
            __BRYTHON__.$py_module_path['__main__'] = window.location.href;
            var $root = __BRYTHON__.py2js($src, '__main__');
            var $jssrc = $root.to_js();

            //eval in global scope
            if (window.execScript) {
               window.execScript($jssrc);
               return;
            }

            //fix for old browsers
            var fn = function() {
                window.eval.call(window, $jssrc);
            };
            fn();
        } 
    });    

});

//init observer, monitoring changes in all nodes of any level
mutationObserver.observe(document, { childList: true, subtree: true });

The code above initiates an observer (MutationObserver) for all elements that will be loaded in the webpage. If a <script> tag is found with language (type attribute) equals text/pyscript, the code will be converted by Bryton. If the tag has the src attribute, we do an Ajax synchronous call to get the source and then run it immediately.

Using in a web page

In order to use Pyscript language in any web page, we need to include both Brython and our MutationObserver implementation, in this order, in the beginning of the HTML code.

See this example:

<html> 
<head> 
<script type="text/javascript" src="brython.js"></script> 
<script type="text/javascript" src="pyscript.js"></script>

And – voilà – we have our own language running in a web page! 😀

A practical example

<!DOCTYPE html>  
<html> 
<head> 
<meta charset="UTF-8"> 
<title>Pyscript Test Page</title>

<!-- init brython and pyscript --> 
<script type="text/javascript" src="brython.js"></script> 
<script type="text/javascript" src="pyscript.js"></script>

<!-- set javascript variable --> 
<script type="text/javascript"> 
var value = 1; 
</script>

<!-- python: print text and javascript variable --> 
<script type="text/pyscript"> 
print('Print Test!!!') 
print(value) 
</script>

</head>

<body> 
    <div id="content">Content Here</div> 
</body>

<!-- python: browser interaction --> 
<script type="text/pyscript"> 
from _browser import doc, alert

alert('Variable value: ' + str(value))

mylist = ['items', 'of', 'mylist'] 
doc['content'].text = 'Content from python: ' + ' '.join(mylist) 
</script>

<!-- python: execute external script --> 
<script type="text/pyscript" src="myscript.py"></script>

</html>

In the page above, after including the two dependencies in the beginning, there’s many script tags. I’ll explain what each one does:

  • The first one runs Javascript and sets the value variable.
  • The second runs Python and prints the value variable in the browser console.
  • The third runs Python and interacts with the browser, showing an alert box and manipulating the text of the <div id="content"> element.
  • The fourth runs Python from an external file.

The content of myscript.py file is very simple. have a look at it:

d = { '11': 'one', '22': 'two' }
for i in d: print(i, '=', d[i])

The script above creates a dictionary d (dict) and then outputs its keys and values in a for loop.

If you look back to the third script, you’ll notice the _browser library. It’s from Brython and gives us access to browser features in a simple and straightforward manner.

Notice that Brython doesn’t have all its libraries in the main script we included in the HTML. For each import it downloads the corresponding library, unless it was previously loaded in a previous import or included in a <script> tag.

Check it out

Do you want to see a working example? It’s available in my GitHub site: http://utluiz.github.io/pyscript/!

Note: do not forget to open the developer tools of your browser (F12) and check the console output!

Wanna see the source code? It’s availabel in my GitHub account: https://github.com/utluiz/utluiz.github.io!

Performance issues

At this point, good engineers should be very worried about the performance of this solution.

Well, you probably know about a lot of initiatives from bowser developers (Google, Mozilla, Microsoft) to improve Javascript performance. Today, running code in a browser is not a problem anymore. But is it enough to support something like a new language?

Obviously the conversion from Python to Javascript is very, very slow! On the other hand, Brython caches the translated code. And it gets even better. Looking Brython source code over, I found that it uses HTML5 local storage API to cache translated scripts, so the cache persists through page reloading.

“And about the runtime?”, you can ask. The final code is pure Javascript, so the browser is able to do the same optimizations it does with native Javascript code! Well, of course it doesn’t mean it’ll be as efficient as native code, after all there is a natural overhead due to instruction “adaptation” and each Python instruction can be translated into several Javascript commands.

Limitations

Brython has some limitations.

For instance, Javascript code can’t access objects and variables defined in Python. But I think it’s not a great need anyway.

Another point is about inline code as I have mentioned in the beginning. For now, we can’t do something like this:

<button onclick="python: print(1)">Button</button>

Indeed, it’s not impossible. We could manipulate the onclick attribute in our MutationObserver implementation, replacing the value by a Javascript function that delegates the execution to Brython!

Well, this article is bit long already, so I’ll assign it to the reader as homework. 😉

Anyway, you should know that is much better don’t use event attributes. It’s a good practice doing unobstructive Javascript through listener. Brython docs tell us how we can do that:

btn.bind('click', show)

Browser compatibility

According to the documentation, MutationObserver is compatible with the following browsers:

  • Google Chrome: version 26 or greater
  • Mozilla Firefox: version 14 or greater
  • Internet Explorer: version 11 or greater
  • Opera: version 15 or greater
  • Safari: version 6 or greater

As always, Internet Explorer is late. However, we’re looking to the future now. In a few years the majority of the IE users will be able to run this solutions without any problem.

Brython tell us nothing about compatibility, but in its source code I found specific implementations to IE 6 and 7. Therefore the limiting factor dwells with MutationObserver.

Final thoughts

In practice, I believe this kind of implementation is not viable for real applications. However, it was a great exercise!

What to expect in the future? Many developers are anxious to run their favorite language!

Today, we have the JVM already supporting many languages like Scala, Ruby, Python, PHP, and so forth. Perhaps, in a near future, we’ll testimony freedom to run any language in a web browser!

Executando Python (ou qualquer outra linguagem) no navegador

browsers-war-java-script-engines

Há alguns dias surgiu uma questão muito interessante no StackOverflow em Português sobre a possibilidade de executar uma linguagem arbitrária num browser, assim como é feito hoje com o Javascript.

Como poderíamos executar a nossa própria linguagem numa página web? Vamos encarar o desafio e implementar isso na prática!

Requisitos

Temos dois métodos para incluir código Javascript numa página web.

Dentro da tag <script>:

<script type="text/javascript">
alert('código javascript executando');
</script>

De uma fonte externa:

<script type="text/javascript" src="script.js"></script>

A ideia é permitir a inclusão de nossa linguagem da mesma forma.

Dentro da tag <script>:

<script type="text/nossalinguagem">
...
</script>

De uma fonte externa:

<script type="text/nossalinguagem" src="nosso-script.abc"></script>

Não estou ainda considerando código inline, como às vezes usado em eventos onclick, onchange, etc.

No entanto, existem particularidades na execução de tags <script>:

  • Elas são executadas sequencialmente, na ordem em que aparecem no HTML.
  • Se um script é incluído com o atributo src, o mesmo deve ser baixado e executado antes do navegador continuar a exibir o restante da página ou executar outros scripts. Este é o motivo pelo qual os scripts incluídos em um site devem ficar no final da página (perto do </body>) sempre que possível.

Nossa linguagem deve co-existir com o Javascript numa página HTML, como no trecho abaixo:

<script type="text/javascript">
var a = 6;
</script>
<script type="text/nossalinguagem">
print(a) # deve mostrar 6
</script>

Análise da solução

Como o navegador por padrão ignora linguagens que ele não conhece, não há problemas em declarar tags <script> com um atributo type qualquer. Entretanto, como fazer o navegador executá-lo?

Inicialmente, havia pensado que a única solução seria criar um loader em Javascript capaz de carregar a página e processar o conteúdo, adicionando programaticamente o HTML ao DOM e executando os scripts encontrados. Isso nada mais é que do uma versão do PHP ou algum template processor em Javascript, não é mesmo?

Porém, os navegadores modernos disponibilizam uma API que permite interceptar alterações na página, incluindo a criação de tags durante o carregamento do HTML. Trata-se do MutationObserver.

As possibilidades são extraordinárias!

Usando o MutationObserver

Para usar o MutationObserver, basta instanciá-lo passando em seu construtor uma função listener, a qual receberá como parâmetro as alterações na página, e invocar o método observe informando o elemento que desejamos monitorar, neste caso, todo o documento.

Vejamos um exemplo básico:

// cria instância do observer
var mutationObserver = new MutationObserver(function(mutations) {

    // itera sobre as alterações no documento
    mutations.forEach(function(mutation) {

        // recupera nós adicionados
        var addedNodes = mutation.addedNodes;

    });

});

// inicia monitoração das alterações de nós em qualquer nível do documento
mutationObserver.observe(document, { childList: true, subtree: true })

Só resta verificar se os nós adicionados são da nossa nova linguagem e delegar a execução a um interpretador.

Definindo uma linguagem: Python

Mas qual linguagem utilizar? Deveria ser uma linguagem de alto nível e poderosa, para obter um ganho de produtividade em relação ao Javascript.

Python me parece uma boa escolha!

Construindo (ou emprestando) um interpretador

O primeiro passo seria construir um interpretador em Javascript para nossa linguagem escolhida. Porém, não vamos reinventar a roda! Há diversas implementações desta linguagem em diversas plataformas. Para Javascript temos o Brython, uma implementação do Python 3.

Este projeto já cumpre parcialmente nossos requisitos quanto à interpretação da linguagem em tags <script> e acesso aos valores do Javascript. Porém, os scripts em Python são executados somente após o carregamento de toda a página através do evento onload do body e não conforme a sequência das tags <script> como desejamos.

Veja o exemplo de uso original do Brython:

<body onload="brython({debug:0, cache:'none'})">

Então vamos usarmos o MutationObserver para delegar a execução dos scripts ao Brython assim que o script for encontrado na página, oferecendo uma integração melhor da linguagem e atendendo os requisitos propostos.

Delegando a execução ao Brython

Nossa linguagem depende do interpretador Brython, então precisamos saber como delegar a execução dos scripts para ele. A má notícia é que o Brython não disponibiliza um meio direto para isso. A boa notícia é que lidamos com um projeto open source, então nada melhor que inspecionar o fonte para descobrir como ele faz isso!

Tudo o que precisamos é do trecho:

//define o nome do módulo como 'main' por que está na página principal
**BRYTHON**.$py_module_path['**main**'] = window.location.href;

//executa o analisador/conversor para Javascript 
var $root = **BRYTHON**.py2js($python_source, '**main**');

//recupera o código Javascript 
$javascript = $root.to_js();

Definindo um nome para nossa linguagem de script: Pyscript

Nossa linguagem de script merece um nome próprio. Como luizscript é muito feio, decidi calcular a equação:

Javascript – Java + Python = Pyscript

Então, nossa tag de script será:

<script type="text/pyscript"></script>

Adicionando suporte à linguagem Pyscript

Com toda a bagagem que juntamos até aqui já podemos criar a implementação do MutationObserver para dar suporte ao Pyscript em uma página web.

Vamos criar o arquivo pyscript.js:

//init brython 
brython();

//create observer instance
var mutationObserver = new MutationObserver(function(mutations) {

    //iterate over document changes
    mutations.forEach(function(mutation) {

        //get new node
        var node = mutation.addedNodes[0];

        //is it the type we want?
        if(node && node.tagName === 'SCRIPT' && node.type === 'text/pyscript') {

            //test log
            console.log('Pyscript found!');

            //python source
            var $src;

            //If src attribute is found, do a synchronous ajax to get 
            //the code in order to execute it immediately
            if (node.src!=='') {

                if (window.XMLHttpRequest){
                    // for IE7+, Firefox, Chrome, Opera, Safari
                    var $xmlhttp = new XMLHttpRequest();
                } else {
                    // for IE6, IE5
                    var $xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                }
                $xmlhttp.open('GET', node.src, false);
                $xmlhttp.send();
                if ($xmlhttp.readyState === 4 && $xmlhttp.status === 200) {
                    $src = $xmlhttp.responseText;
                }
                if ($src === undefined) { // houston, we have a problem!!!
                    console.log('Error loading pyscript: ' + node.src);
                    return;
                }

            } else {

                //without src, source is tag content
                $src = node.textContent || node.innerText;

            }

            //python -> javascript
            __BRYTHON__.$py_module_path['__main__'] = window.location.href;
            var $root = __BRYTHON__.py2js($src, '__main__');
            var $jssrc = $root.to_js();

            //eval in global scope
            if (window.execScript) {
               window.execScript($jssrc);
               return;
            }

            //fix for old browsers
            var fn = function() {
                window.eval.call(window, $jssrc);
            };
            fn();
        } 
    });    

});

//init observer, monitoring changes in all nodes of any level
mutationObserver.observe(document, { childList: true, subtree: true });

O código acima inicia um observador (MutationObserver) para todos os elementos da página. Se uma tag <script> for encontrada cuja linguagem (atributo type) for text/pyscript, então o código será delegado ao Brython. No caso da tag ter o atributo src, nos dizendo que o código Python está em um arquivo externo, então fazemos uma chamada Ajax síncrona para recuperar o código e executá-lo imediatamente.

Aplicando numa página web

Para a utilização da nossa linguagem Pyscript em uma página qualquer, precisamos incluir tanto o Brython quanto nossa implementação do MutationObserver, nesta ordem, logo no início do código HTML.

Vejamos o exemplo:

<html> 
<head> 
<script type="text/javascript" src="brython.js"></script> 
<script type="text/javascript" src="pyscript.js"></script>

E – voilà – temos nossa própria linguagem rodando na página!

Um exemplo prático

<!DOCTYPE html>  
<html> 
<head> 
<meta charset="UTF-8"> 
<title>Pyscript Test Page</title>

<!-- init brython and pyscript --> 
<script type="text/javascript" src="brython.js"></script> 
<script type="text/javascript" src="pyscript.js"></script>

<!-- set javascript variable --> 
<script type="text/javascript"> 
var value = 1; 
</script>

<!-- python: print text and javascript variable --> 
<script type="text/pyscript"> 
print('Print Test!!!') 
print(value) 
</script>

</head>

<body> 
    <div id="content">Conteúdo</div> 
</body>

<!-- python: browser interaction --> 
<script type="text/pyscript"> 
from _browser import doc, alert

alert('Variable value: ' + str(value))

mylist = ['items', 'of', 'mylist'] 
doc['content'].text = 'Content from python: ' + ' '.join(mylist) 
</script>

<!-- python: execute external script --> 
<script type="text/pyscript" src="myscript.py"></script>

</html>

Nesta página, após incluir nossas dependências no início, existem várias tags de script:

  • A primeira executa Javascript que inicializa a variável value.
  • A segunda executa Python e imprime a variável value no console do navegador.
  • A terceira executa Python e interage com o navegador, exibindo uma caixa de alerta e manipulando o conteúdo (texto) do elemento <div id="content">.
  • A quarta e última executa Python a partir de um arquivo externo.

O conteúdo do arquivo myscript.py é bem simples. Vejamos:

d = { '11': 'um', '22': 'dois' }
for i in d: print(i, '=', d[i])

O script acima cria um “dicionário” d (dict) e então exibe chaves e valores de seus elementos num loop for.

Voltando um pouco ao terceiro script, note que importamos a biblioteca _browser, pertencente ao Brython. Ela nos dá uma interface para acessar as funcionalidades do navegador de forma simples e direta.

É importante observar que o Brython não contém todas as bibliotecas em seu script principal. Para cada import é feito o download da respectiva biblioteca, a não ser que a mesma já tenha sido carregada em um import anterior ou incluída numa tag <script>.

Assista ao vivo

Quer ver o exemplo funcionando? Ele está disponível no link http://utluiz.github.io/pyscript/!

Nota: não se esqueça de acessar a ferramenta do desenvolvedor (F12) para verificar o console!

Quer o código-fonte? Ele está disponível na minha conta do Github: https://github.com/utluiz/utluiz.github.io!

Algumas considerações sobre desempenho

A esta altura, um desenvolvedor mais experiente já estaria se coçando todo sobre a questão do desempenho dessa solução.

Existem iniciativas das empresas que desenvolvem navegadores (Google, Mozilla, etc.) para criar soluções que melhoram o desempenho do Javascript. Hoje, executar código no navegador não é mais um problema. Mas será que isso é suficiente para suportar algo tão profundo quanto uma nova linguagem?

Obviamente, a conversão de código Python para Javascript é muito lenta! Por outro lado, o Brython coloca o código-fonte convertido em um cache para não precisar realizar o processo novamente. Ao analisar o código-fonte do Brython me deparei com soluções de armazenamento usando a API local storage do HTML5, então mesmo se a página for recarregada a conversão não será feita novamente para o mesmo bloco de script.

Mas e quanto à execução? Se o código final executado é em Javascript, então o navegador consegue fazer as mesmas otimizações que no código nativo! Bem, mas isso não significa que será tão eficiente quanto código nativo, afinal existe um overhead natural, pois cada instrução Python pode gerar várias instruções em Javscript.

Limitações da solução

Devido a limitações do Brython, o código Javascript não consegue acessar os objetos e variáveis definidos no código Python. De qualquer forma, não consigo ver isso como uma grande necessidade.

Outra limitação é quanto a código inline mencionado no início do artigo. Por enquanto, não podemos fazer algo como no código abaixo:

<button onclick="python: print(1)">Botão</button>

Não que seja impossível. Poderíamos manipular o valor do atributo onclick dentro do MutationObserver, substituindo-o por uma função Javascript que delega a execução do código para o Brython! Bem, já está meio tarde, então deixo isso como lição de casa. 😉

De qualquer forma, essa limitação é facilmente contornada removendo o atributo onclick e adicionando um listener ao respectivo evento através de código Python, como nos é informado na própria documentação do Brython:

btn.bind('click', show)

Compatibilidade com navegadores

Segundo a documentação, o MutationObserver é compatível com o seguintes navegadores:

  • Google Chrome: versão 26 em diante
  • Mozilla Firefox: versão 14 em diante
  • Internet Explorer: versão 11 em diante
  • Opera: versão 15 em diante
  • Safari: versão 6 em diante

Como sempre, o mais “atrasadinho” é o Internet Explorer. Porém, estamos olhando para o futuro. Em alguns anos a maioria dos usuários do IE poderá executar nossa solução sem dificuldades.

A documentação do Brython não informa sobre sua compatibilidade, mas olhando o código-fonte encontrei implementações específicas para IE 6 e 7. Portanto, a limitação deve ficar mesmo com o MutationObserver.

Considerações finais

Na prática, creio ainda não ser viável aplicar esse tipo de solução em aplicações reais. Por outro lado, é um ótimo exercício!

O que podemos esperar do futuro? Muitos desenvolvedores estão ansiosos por rodar suas linguagens prediletas no navegador!

Hoje JVMs como a do Java já executam diversas linguagens como Scala, Ruby, Python, PHP e assim por diante. Quem sabe, em breve, poderemos testemunhar a ascensão da liberdade de linguagem dentro dos navegadores web!

Página 6 de 10

Creative Commons O blog State of the Art de Luiz Ricardo é licenciado sob uma Licença Creative Commons. Copie, compartihe e modifique, apenas cite a fonte.