Git como boa prática de consultoria

Últimamente tenho utilizado bastante o git, mas não usando convencionalmente versionando apenas projetos, mas sim diretórios que julgo que contenham arquivos que se modificados podem me render um bom trabalho, como o /etc por exemplo.

Gostei tanto dessa utilização que resolvi adotar como uma boa prática de consultoria. É muito comum clientes se queixando que o que você deixou funcionando a algum tempo parou de funcionar e quando você começa a analisar o problema ,as vezes com bastante custo, descobre que alguém resolveu por algum motivo alterar um arquivo ou adicionar uma biblioteca que não deveria. Com o uso do git conseguimos contornar isso.

O git trabalha de maneira que os commits são feitos apenas localmente e só quando feito um git push que os arquivos versionados serão enviados ao servidor. Logo como não adicionamos nenhum servidor para fazer push e nem iremos tentar fazer push não corremos o risco desses arquivos serem publicados. Segue um exemplo básico:

cd /opt/jboss7

git init
Initialized empty Git repository in /opt/jboss7/.git/

git add *

git status

# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: LICENSE.txt
# new file: README.txt

.....

# new file: welcome-content/noconsole.html
# new file: welcome-content/noredirect.html

git commit -am 'Servidor de Aplicação Jboss AS 7.'

-a = all files
-m = commit message

Quando rodamos o git init o diretório oculto .git é criado de onde o comando foi invocado, é nesse diretório que ficam os metadados do git, logo vale a pena se atentar o tamanho que esse diretório pode ficar dependendo do tamanho do diretório é versionado, veja o tamanho que ficou para o versionamento do jboss7 completo.

[rodrigoramalho]  /opt/jboss7
du -sh .git/
75M .git/

Com o projeto versionado, se rodarmos um git status veremos que não há nada a ser comitado (se nada tiver sido alterado claro). Então é isso, a medida que alterações forem sendo feitas basta ir fazendo novos commits e quando acontecer a situação que mencionei no começo do post um simples git status já acusaria se algum arquivo foi adicionado/alterado/removido e ainda nos permitiria visualizar as alterações a até mesmo fazer um rollback.

Com os commits devidamente comentados caso outra pessoa assuma a consultoria, apenas pelos logs do git já consegue ter um bom resumo do que já foi feito.

Cola dos principais comandos:

# Exibe o log de uma forma melhor pra visualizar
git log --oneline

# Exibe o status de uma forma melhor pra visualizar
git status -s

git diff <commit hash> <filename>
# o commit hash é exibido no git log.

# Faz rollback no arquivo para a versão informada
git reset <commit hash> <filename>

, ,

Deixe um comentário

Remover Protetor de Link

Vira e mexe a gente se depara com uns problemas no dia dia bem irritantes, como por exemplo quando você vai baixar um episódio do seu seriado preferido abre uma página pedindo pra você cadastrar seu celular ou baixar um programa tipo “pcmegarápido” e só após disso o link para o download é disponibilizado:

Imagem

Então fiz uma aplicação que descriptografa o link, devolvendo um novo contendo o caminho direto para o download, ou seja, sem precisar cadastrar o bendito celular ou baixar o “pcmegarapido.exe”.

http://desprotetorlink.com.br

Basta copiar a url “bloqueada” e colar no único campo que a aplicação apresenta.

Imagem

Enfim, quem tiver interesse em saber como funciona sugiro dar uma olhada no README e no código no github, sintam-se a vontade pra colaborar d;-)

, , , ,

Deixe um comentário

Configurando proxy no Git

Quando estamos em ambientes corporativos é muito comum o uso de proxy e consequentemente acabamos tendo algum problema com o git. Aqui estão alguns passos para configurar proxy no git.

git config –global http.proxy meu.servidor:8080

As vezes o github não consegue validar o certificado digital, para desabilitar:

git config –global http.sslverify false

Geralmente adicionamos a url do repositório para usar o protocolo git (git remote add origin git@github.com:hodrigohamalho/blablabla.git), como setamos proxy apenas para http e https, se a url estiver para utilizar git devemos alterar.

Para visualizar a url atual:
git remote show origin

Para alterar:
git remote set-url origin https://github.com:hodrigohamalho/blablabla.git

Com essa alteração tudo já deveria estar funcionando.

Como as configurações são globais, todos os projetos irão utilizar o proxy setado, para remover utilize:
git config –unset –global http.proxy

git config –unset –global http.sslverify

That’s all folks ;P

, , ,

Deixe um comentário

Teste em aplicações Java com Rspec

Durante o período que trabalhei na SEA Tecnologia, fui apresentado ao desenvolvimento Ágil e o desenvolvimento de aplicações usando TDD. No início participei de alguns projetos em Java, posteriormente conheci o Ruby on Rails e comecei a fazer testes com o RSpec, confesso que fiquei fascinado com a integração, facilidade e clareza que essa ferramenta de testes apresenta. Com o JRuby, projeto que compila código Ruby pra bytecode Java, podemos não só executar Ruby através da JVM, como fazer a integração com código Java. Existe um projeto que permite essa integração (Java x Ruby) utilizando maven. Então meu objetivo com esse post é mostrar como fazer essa integração, e escrever alguns testes em Ruby testando classes Java.

Pré requisito:
Maven

Instalação Jruby
Adicionar a variável de ambiente JRUBY_HOME
Edite o arquivo ~/.bash_profile, adicionando as seguintes linhas:

export JRUBY_HOME=/opt/jruby-1.3.1

Hands on, vamos começar a criação de um game, snake game o famoso jogo da cobrinha nos celulares mais antigos =P

Criando o projeto maven:

mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=org.rodrigoramalho.snake-game -DartifactId=snake-game
# Quando aparecer essa linha, apenas aperte enter.
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 16:
# Nessa também
Define value for property 'version':  1.0-SNAPSHOT: :

Depois basta confirmar (Y) e pronto o projeto estará criado.
Entre no diretório e execute a task do maven para gerar os arquivos necessários para importamos o projeto no eclipse.

cd snake-game/
mvn eclipse:eclipse

Já com o projeto importado no eclipse, ediciona o plugin do RSpec no pom.xml

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rspec-maven-plugin</artifactId>
<configuration>
<jrubyHome>${env.JRUBY_HOME}</jrubyHome>
<sourceDirectory>${basedir}/src/test/specs</sourceDirectory>
<outputDirectory>${basedir}/target</outputDirectory>
<systemProperties>
<property>
<name>testProp</name>
<value>testValue</value>
</property>
</systemProperties>
</configuration>
<executions>
<execution>
<id>test</id>
<phase>test</phase>
<goals>
<goal>spec</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Crie o diretório “specs” dentro da pasta test.
snake-game/src/test/specs  Pronto, chegando aqui seu ambiente já está (ou deveria estar, rs) configurado para começar o desenvolvimento. Então, go go go!

Criando a classe de testes em Ruby: (Como o eclipse não tem syntax highlight nativo pro Ruby, eu prefiro usar um editor de texto comum: vim, gedit, textmate)

snake-game/src/test/specs/snake_spec.rb

import org.rodrigoramalho.snakegame.model.Snake;

describe Snake do

end

Para executar o teste, rode:

mvn test

O seguinte erro vai ocorrer:

[INFO] Running RSpec tests from /Users/rodrigoramalho/Documents/workspace/snake-game/src/test/specs
/opt/jruby-1.3.1/lib/ruby/1.8/optparse.rb:605:in `get_proxy_or_package_under_package’: cannot load Java class org.rodrigoramalho.snakegame.model.Snake (NameError)
….

Então vamos criar a classe Snake.


package org.rodrigoramalho.snakegame.model;

public class Snake {

}

Rodamos os testes novamente, com mvn test.

[INFO]
[INFO] — rspec-maven-plugin:1.0-beta-6:spec (test) @ snake-game —
[INFO] Running RSpec tests from /Users/rodrigoramalho/Documents/workspace/snake-game/src/test/specs
=========================================
TOTAL: 0 passing; 0 failing; 0 pending

Qualquer alteração feita nessa classe, não é necessário rodar a task “mvn test” novamente, dentro do diretório snake-game/target/ contém um arquivo com o nome de run-specs.sh que recompila as classes e executa os testes. Utilizando esse script é economizado um bom tempo, pois roda bem mais rápido que o “mvn test”.

Os relatórios dos testes são gerados no arquivo snake-game/target/rspec_report.htmlDepois de fazer alguns testes segue o código da classe ruby. (A identação desse plugin ta zuada :/)


import org.rodrigoramalho.snakegame.model.Snake;
import org.rodrigoramalho.snakegame.model.Direcao;

describe Snake do

 before(:each) do
 @snake = Snake.new "3 5 5 NORTE"
 end

context "com parametros válidos" do

 it "deveria receber o comprimento e as coordenadas iniciais" do
 @snake.comprimento.should == 3
 @snake.posicao.x.should == 5
 @snake.posicao.y.should == 5
 @snake.posicao.direcao.should == Direcao::NORTE
 end

end

context "com parametros invalidos" do
 it "direção inválida" do
 expect{ Snake.new "5 5 5 Rodrigo" }.should raise_error(java.lang.IllegalArgumentException)
 end

 it "comprimento negativo" do
 expect{ Snake.new "-5 5 5 NORTE" }.should raise_error(java.lang.IllegalArgumentException)
 end

 it "posicao x negativa" do
 expect{ Snake.new "5 -5 5 NORTE" }.should raise_error(java.lang.IllegalArgumentException)
 end

 it "posicao y negativa" do
 expect{ Snake.new "5 5 -5 NORTE" }.should raise_error(java.lang.IllegalArgumentException)
 end
 end

 context "movimentação" do
 context "com movimentos possiveis" do
 it "deveria virar para esquerda quando estiver na direcao norte" do
 @snake.virarEsquerda();
 @snake.posicao.direcao.should == Direcao::OESTE
 end

 it "deveria virar para esquerda quando estiver na direcao sul" do
 @snake.posicao.direcao = Direcao::SUL
 @snake.virarEsquerda();
 @snake.posicao.direcao.should == Direcao::OESTE
 end

 it "deveria continuar apontando para esquerda ao tentar virar para esquerda" do
 @snake.posicao.direcao = Direcao::OESTE
 @snake.virarEsquerda();
 @snake.posicao.direcao.should == Direcao::OESTE
 end

 it "deveria virar para direita quando estiver na direcao norte" do
 @snake.virarDireita();
 @snake.posicao.direcao.should == Direcao::LESTE
 end
 end

 context "com movimentos impossíveis" do

 it 'quando apontando para o LESTE não deveria virar para a ESQUERDA' do
 @snake.posicao.direcao = Direcao::LESTE
 @snake.virarEsquerda();
 @snake.posicao.direcao.should == Direcao::LESTE
 end

 end
 end

end

Classe Java (Se quiser visualizar com detalhes, veja o fonte do código no github)


package org.rodrigoramalho.snakegame.model;

import org.rodrigoramalho.util.ValidacaoUtil;

public class Snake {

private Integer comprimento;

private Posicao posicao;

public Snake(){}

public Snake(String coordenadas){

String[] args = coordenadas.split(" ");

this.comprimento  = new Integer(args[0]);

getPosicao().setX(new Integer(args[1]));

getPosicao().setY(new Integer(args[2]));

getPosicao().setDirecao(Direcao.valueOf(args[3]));

if (ValidacaoUtil.isMenorQueZero(getComprimento(), getPosicao().getX(), getPosicao().getY()))

throw new IllegalArgumentException("Use apenas valores > 0");

}

public void virarEsquerda(){

if (getPosicao().getDirecao() != Direcao.LESTE){

getPosicao().setDirecao(Direcao.OESTE);

}

}

public Integer getComprimento() { return comprimento; }

public void setComprimento(Integer comprimento) { this.comprimento = comprimento; }

public Posicao getPosicao() {

if (posicao == null)

posicao = new Posicao();

return posicao;

}
public void setPosicao(Posicao posicao) { this.posicao = posicao; }

}

É isso ai! Depois aconselho a explorarem mais os testes com RSpec porque tem bastante coisa legal, e ainda tem o poder de fazer os testes usando 100% código Ruby, desfrutando de vários benefícios como Closures por exemplo :)

Fonte do projeto de exemplo.

, , , , , ,

Deixe um comentário

Inserir tag no cabeçalho html do Portal Gatein

Esses dias precisei inserir uma tag no cabeçalho de todas as páginas que estivessem no portal gatein.

Existem duas maneiras de fazer isso:

1. Criando um portlet que insere essa tag no <head> da página, carregando pelo método doHeader.

o código do portlet deverá ser semelhante a esse:

public class HeaderExtension extends GenericPortlet {

@Override
protected void doHeaders(RenderRequest request, RenderResponse response) {
Element meta = response.createElement("meta");
meta.setAttribute("http-equiv", "X-UA-Compatible");
meta.setAttribute("content", "IE=edge");
response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, meta);
}

@Override
public void doView(RenderRequest request, RenderResponse response) {
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/index.jsp");
try {
prd.include(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Com isso basta colocar este portlet no layout do site e ele irá colocar toda vez essa tag no cabeçalho da página. Pode ser usado também pra carregar css, javascript, etc.. (Veja mais)
Porém com essa abordagem tudo que for inserido aparece no final da tag head, ou seja, próximo ao /head. As vezes é necessário que a tag esteja no começo do head, como no caso da tag:

 <meta http-equiv="X-UA-Compatible" content="IE=edge"/>

Que é utilizada para compatibilidade com o Internet Explorer.
Com este cenário temos que alterar o fonte do template groovy que o portal utiliza. O arquivo pode ser encontrado em:

$JBOSS_EPP_HOME/server/$SERVER_NAME/deploy/gatein-wcm-extension-2.1.5-CP02.ear/ecm-wcm-extension.war/groovy/portal/webui/workspace/UIPortalApplication.gtmpl

Ps: É necessário explodir o arquivo gatein-wcm-extension-2.1.5-CP02.ear e o ecm-wcm-extension.war

Feita a alteração neste template groovy basta reiniciar o portal.

, , , , , ,

Deixe um comentário

Bash Script find and replace

Faz substituição no texto de arquivos.
A linha de comando abaixo faz uma procura e substitui todas as ocorrências em arquivos textos da palavra “procure_por_isso” nos diretórios abaixo de “/home/meu_diretorio” e substitui por ”substitua_por_isso”.
.
find "/home/meu_diretorio" -type f -exec sed -i "" -e s/procure_por_isso/"substitua_por_isso"/g {} \;
.
.
Faz substituição no nome de arquivos e diretórios
O script abaixo renomeia todos os arquivos e diretórios com o nome de “procure_por_isso” e substitui por “substitua_por_isso” a partir do diretório “/home/procure_por_aqui”
.
  FILES=$(find /home/procure_por_aqui -name "procure_por_isso")
  for FILE in $FILES; do
    NAME="$FILE"
    NAME_REPLACED=$(echo $FILE | sed -e s/"procure_por_isso"/"substitua_por_isso"/g)
    mv $NAME $NAME_REPLACED
  done
.
Cabe lembrar que caso queira pesquisar pela parte do nome, no parametro -name pode-se utilizar o: *
por exemplo:
.
FILES=$(find /home/procure_por_aqui -name *procure_por_isso*)
.
Vai procurar por qualquer ocorrência de “procure_por_isso”. Segue um exemplo de alguns resultados possíveis:
.
abc_procure_por_isso
procure_por_isso_abc
procure_por_isso
.

, , , , , ,

Deixe um comentário

Introdução a Cluster

Cluster ?

Cluster é o ato de de rodar a mesma aplicação em vários servidores de aplicação simultaneamente com cada aplicação estando ciente das outras que estão no cluster. Um servidor de aplicação em um cluster é chamado de nó.

Pra quê ?

Creio que a melhor forma de entender o por que? é com um exemplo, então:

O servidor em que minha aplicação está rodando suporta 1000 usuários simultâneos, porém hoje 2000 usuários simultâneos acessaram minha aplicação e o servidor caiu.
E agora José ? Temos 2 opções, ou adquiro recursos para o servidor que já está rodando ou adiciono outras máquinas para responderem esses requests, desafogando a primeiro servidor.

Cluster: Vertical x Horizontal

Clusters podem ser formados com nós rodando em uma máquina ou em várias máquinas. Essa formação do cluster é comumente se referida como topologia.

  • Horizontal: Quando os nós do cluster estão em diferentes máquinas.
  • Vertical: Quando os nós do clusters estão na mesma máquina

No cluster horizontal  se acontece alguma falha com a máquina (ex: queda de energia, queimar algum periférico, etc) a outra máquina assume o cluster sem problemas. Se a máquina do cluster vertical ocorrer algum problema todos os servidores que estavam rodando nela, serão comprometidos. Em outras palavras um cluster horizontal é uma melhor opção para alta disponibilidade.

Balanceamento de Carga

Balanceamento de carga é a maneira de distribuir a carga de entrada entre os diferentes servidores de aplicação, fazendo com que sua aplicação seja escalável e tenha alta disponibilidade. Escalabilidade é o termo usado para descrever a habilidade de fazer com que sua aplicação manipule mais carga adicionando hardware e/ou criando instâncias redundantes sem alterar o código. Então um cluster sem balanceamento de carga faz pouco ou nenhum sentido.

Alta disponibilidade

Caso se tenha uma aplicação que seja toda stateless, pode-se garantir alta disponibilidade apenas colocando um balanceador de carga na frente de vários servidores com a mesma aplicação deployada (note, sem cluster). Esse mecanismo é conhecido como Failover. Cabe a ressalva que no failover nenhum tipo de estado da aplicação é replicado.

Porém se a aplicação for stateful o problema é mais em baixo, imagine que você esteja em um site de compras o seu carrinho já possui 10 itens derrepente o servidor cai, você não vai gostar nada de quando clicar no próximo botão cair em um tela de login e o seu carrinho aparecer zerado. O failover não replica estado então não seria adequado para esse tipo de situação para isso temos o mecanismo a seguir:

Replicação e Tolerância a falhas (Replication e Fault Tolerance)

Um servidor com tolerância a falhas promove alta disponibilidade e continua se comunicando com o cliente mesmo que o servidor caia, ou seja o estado do cliente é mantido. Então no exemplo anterior se o cluster tiver tolerância a falhas então o servidor cairá o cliente será balanceado para outro nó e continuará logado na aplicação com seu carrinho de compras com todos os itens como se nada tivesse acontecido.
Mas o que contém nesse estado do cliente?
Basicamente duas coisas: Dados de sessão e de entidades.

Dados de sessão (Session data) são mantidos em memória pela aplicação ou por mecanismos de cache habilitados pelo servidor de aplicação.
Dados de entidade (Entity data) são mantidos em banco de dados.

Para ser tolerante a falhas o estado associado a uma aplicação deve redundantemente disponível, ou seja, os nós devem replicar o estado entre cada nó do cluster

Fault tolerance = fail over + state replication

É muito comum escutarmos conversas de pessoas pensando em cluster como uma forma de melhorar performance, e como podemos ver cluster é sinônimo de disponibilidade e dependendo do cluster pode é trazer défict de performance devido o fato do servidor de aplicação ter que ficar replicando estado, sincronizando cache, etc.

Existem diferentes formas dessa replicação de estado acontecer em um cluster, mas para não deixar o post muito longo vou encerrando por aqui.

Fonte:
JBoss in Action – Chapter 12 – Understanding Cluster.

8 Comentários

jQuery placeholder plugin (Twitter Like)

Recentemente programando com minha amiga Carolina Mascarenhas ela fez um placeholder bem parecido com o do twitter, apenas dei uma acabada no código e coloquei no formato de plugin.

Placeholder é quando o label de um campo input está localizado dentro do pŕoprio input. Como podemos ver na imagem abaixo:

 

 

Segue a página contendo o exemplo: http://jspace.com.br/jquery-placeholder-plugin/

Como utilizar:

Adicionar os arquivos jquery.js, jquery.placeholder.js e o placeholder.css a página.

<script type="text/javascript" src="jquery.min.js" ></script>
<script type="text/javascript" src="jquery.placeholder.js" ></script>
<link href="placeholder.css" type="text/css" rel="stylesheet" />

O html deve respeitar a seguinte estrutura:

<div class="holding username">
<input type="text" id="username" name="username" />
<span class="holder">Email</span>
</div>

Onde o conteúdo que estiver dentro da tag <span> será o que aparecerá dentro do input.

Tendo isso como premissa, o código para adicionar o placeholder deverá ser o seguinte:

 jQuery(".username").placeholder();

Onde “.username” é a classe da div. Também pode ser utilizado o id.

Algumas opções podem ser especificadas:

jQuery(".username").placeholder({
color_onfocus: "b9b9b9",
color_background_onfocus: "white"
});

Cabe lembrar que o HTML5 tem suporte nativo ao placeholder bastando apenas colocar a propriedade placeholder=”Rodrigo Ramalho” dentro do input.

exemplo:

<input type="text" name="exemplo" placeholder="Demonstrando placeholder" />

Simples assim. Porém nem todos os browsers suportam HTML5 ainda.

Pra quem quiser contribuir e/ou entender melhor o projeto, o source está no meu github:

https://github.com/hodrigohamalho/jquery-placeholder-plugin

, ,

Deixe um comentário

Cheat Sheets

Para evitar o problema de ter que ficar dando uma googlada pra lembrar 'aquele' comando
que está lhe falhando a memória,  basta aprender a explorar os "cheat sheets". Cheat sheet 
é um resumo dos principais comandos de uma determinada API. Hoje precisei lembrar alguns
comandos do heroku e achei esse cheat sheet no qual acrescentei algumas linhas.

Heroku Cheat Sheet 
=== General Commands
 help                         # show this usage
 version                      # show the gem version

 list                         # list your apps
 create [<name>]              # create a new app

 keys                         # show your user's public keys
 keys:add [<path to keyfile>] # add a public key
 keys:remove <keyname>        # remove a key by name (user@host)
 keys:clear                   # remove all keys
 ps                           # display process

=== App Commands (execute inside a checkout directory, or use --app <appname>)

 info                         # show app info, like web url and git repo
 open                         # open the app in a web browser
 rename <newname>             # rename the app
 dynos <qty>                  # set the app to use the specified qty of dynos

 sharing:add <email>          # add a collaborator
 sharing:remove <email>       # remove a collaborator
 sharing:transfer <email>     # transfers the app ownership

 domains:add <domain>         # add a custom domain name
 domains:remove <domain>      # remove a custom domain name
 domains:clear                # remove all custom domains

 ssl:add <pem> <key>          # add SSL cert to the app
 ssl:remove <domain>          # removes SSL cert from the app domain

 rake <command>               # remotely execute a rake command
 console <command>            # remotely execute a single console command
 console                      # start an interactive console to the remote app

 restart                      # restart app servers
 logs                         # fetch recent log output for debugging
 logs:cron                    # fetch cron log output

 maintenance:on               # put the app into maintenance mode
 maintenance:off              # take the app out of maintenance mode

 config                       # display the app's config vars (environment)
 config:add key=val [...]     # add one or more config vars
 config:remove key [...]      # remove one or more config vars
 config:clear                 # clear user-set vars and reset to default

 db:pull [<database_url>]     # pull the app's database into a local database
 db:push [<database_url>]     # push a local database into the app's remote
 database
 db:reset                     # reset the database for the app
-- Postgres databases ---
 pg:info                      # show db info
 pgbackups                    # list postgres backups
 pgbackups:url id             # get url to download backup
 pgbackups:capture            # capture a new backup
 pgbackups:destroy id         # destroy bakup

 bundles                      # list bundles for the app
 bundles:capture [<bundle>]   # capture a bundle of the app's code and data
 bundles:download             # download most recent app bundle as a tarball
 bundles:download <bundle>    # download the named bundle
 bundles:animate <bundle>     # animate a bundle into a new app
 bundles:destroy <bundle>     # destroy the named bundle

 addons                       # list addons
 addons:add name [key=value]  # install addon (with zero or more config vars)
 addons:remove name           # uninstall an addons
 addons:clear                 # uninstall all addons

 destroy                      # destroy the app permanently

=== Example:

 rails myapp
 cd myapp
 git init
 git add .
 git commit -m "my new app"
 heroku create
 git push heroku master

, , , ,

Deixe um comentário