segunda-feira, 23 de julho de 2012

IceScrum?

IceScrum é uma ferramenta interessante para gerenciamento do processo ágil de desenvolvimento de software chamado Scrum. Desenvolvido em Java na plataforma web, o IceScrum é um software de código aberto e grátis (download).

Entre algumas ferramentas grátis que testei para auxiliar no gerenciamento do processo de desenvolvimento, o IceScrum foi a ferramenta que me deixou mais confortável porque implementa os conceitos da metodologia Scrum de forma concisa e quase natural.

Instalação & Configuração

A instalação da ferramente pode ser feita pela implantação de um arquivo WAR no Tomcat, ou noutro servidor compatível, ou então baixar a versão 'bundled' que já vem com o Tomcat embutido e executar scripts para execução.

A versão atual  R5#1.2 dispensa maiores configurações para banco de dados, pois utiliza o banco de dados embutido HSQLDB, também baseado em Java. Apesar desta facilidade, outras configurações ainda são necessárias e as instruções apresentada no site não são tão precisas. Tive problemas com as duas opções de instalação, mas consegui colocar em funcionamento apenas a versão 'bundled' após diversas tentativas (mas isso está fora do escopo deste artigo). O fórum de suporte da ferramenta ajudou, mas tem várias respostas dos próprios administradores do fórum sem explicações técnicas sobre o problema, apenas indicando um download para resolvê-lo.

Funcionalidades

Mesmo não registrado no sistema (logado), qualquer pessoa pode visualizar parcialmente os projetos cadastrados. Esta visualização parcial significa ver o painel inicial do projeto, os registros de atividades e incluindo os gráficos, a área livre (sandbox) e a lista do produto (product backlog); também é possível ver a estórias detalhadamente.

Ainda, qualquer pessoa que acessa o sistema pode fazer um registro, criar um projeto e montar uma equipe para este projeto adicionando usuários cadastrados na ferramenta ao projeto.

Somente após pesquisar no fórum de discussão da ferramenta, descobri que existe um usuário administrador. Este usuário consegue acessar todos os projetos, aparentemente sem restrição, mas não tem o controle dos usuários cadastrados. Isso chegou a ser um problema para mim, pois, aqui, um desenvolvedor chegou a esquecer a identificação e senha da própria conta; eu não tive como pesquisar a sua identificação no sistema, para que mais tarde, o próprio usuário pudesse fazer a sua recuperação de senha.

Gostei bastante da possibilidade de criar estórias por qualquer membro da equipe, ficando estas estórias numa área livre esperando aceitação do dono do projeto ou scrum master. Uma estória pode ser priorizada e também estimadas por 'story points' após aceita. Também é possível adicional testes de aceitação à estórias.

As iterações são programadas de forma fácil, onde as estórias aceitas e estimadas (atribuição de peso) são listadas e adicionadas à iteração via drag-and-drop. A partir de uma estória, criam-se as tarefas que automaticamente assumem a coluna 'Para Fazer' do painel de iteração.

A imagem ao lado mostra o painel de um projeto em andamento; nomes de projeto, de algumas tarefas e de colaboradores foram ofuscados por motivos óbvios! Os post-its das tarefas apresentados no painel podem ser movidos entre as colunas  'Para Fazer', 'Em Andamento' e 'Concluído' via drag-and-drop

Os desenvolvedores do projeto devem mover uma tarefa com estado 'Para Fazer' para a coluna 'Em Andamento'; automaticamente o sistema associa o desenvolvedor à tarefa. Uma vez a tarefa concluída, o post-it deve ser movido para a coluna seguinte. Muito intuitivo!

Problemas...

Uma desvantagem da ferramenta é a quantidade de memória alocada para sua execução. O tamanho de 512 MB é a quantidade mínima recomendada, mas eu tive que alocar um pouco mais. Para fazer isso na versão 'bundled' foi preciso modificar os scripts de execução do Tomcat, o que não está prontamente documentado no site da ferramenta!

Em alguns momento, a ferramenta travou, mesmo com mais de 512MB de memória alocada. Apenas dois projetos com pouquíssimas estórias e tarefas estavam cadastradas na ferramenta. Ainda estou acompanhando e analisando a frequência deste tipo ocorrência.

Ainda, mensagens de erro com a conexão com banco de dados foi apresentada, todavia não foi notado maiores problemas além da mensagem.

Conclusão

IceScrum, versão R5#1.2, é uma ferramenta com uma interface gráfica agradável e até um tanto intuitiva. Sua instalação não foi tranquila para mim e a documentação do site está desatualizada; todavia o fórum de discussão pode ajudar. A alocação de memória superior se comparada à outras ferramentas não chega a ser um impeditivo real, já os problemas de travamento da ferramenta e mensagem de erro de conexão com o banco de dados deixa uma impressão ruim e uma sensação de insegurança para adotar a ferramente integralmente. 

segunda-feira, 2 de julho de 2012

Configurando Launch4J para Maven

Launch4J é um aplicativo famoso usado para transformar arquivos JAR em arquivos executáveis para o sistema operacional Windows. É natural que se queira utilizar este aplicativo em conjunto com o Maven para gerar os arquivos finais do projeto para distribuição. Neste cenário, a intenção é atualizar sempre o número da versão e build do aplicativo automaticamente, antes mesmo de gerar o arquivo executável.

A primeira opção para rodar o Launch4J integrado ao Maven é a utilização de plugin. Nos links de referência, no final deste artigo, estão listados alguns websites para o plugin Launch4J para Maven e outros links com exemplos de uso.

A utilização do plugin pode ser mais limitada (ainda não testei) e ainda talvez se deseja utilizar a configuração já existentes do Launch4J do projeto, sem correr o risco de perder alguma configuração; este motivo justificaria a utilização do Launch4J era como um tarefa do Ant.  Não vou ensinar como criar este arquivo de configuração, pois já está razoavelmente documentado no próprio website do Launch4J. Assim, com um mínimo de adaptação, o Launch4J fica pronto para o Maven, como uma tarefa do Ant, como mostra o código abaixo:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
        <execution>
            <id>launch4j</id>
            <phase>package</phase>
            <goals>
                <goal>run</goal>
            </goals>
            <configuration>
                <tasks>                                
                    <copy file="config.xml" tofile="config-temp.xml"/>                                
                    <replace file="config-temp.xml" token="0.0.0.0" value="${project.version}.${BUILD_NUMBER}"/>
                    <taskdef name="launch4j" classname="net.sf.launch4j.ant.Launch4jTask" classpath="${launch4j.dir}/launch4j.jar:${launch4j.dir}/lib/xstream.jar" />
                    <launch4j configFile="./launch4j-client-temp.xml" jar="${project.build.directory}\my-application-${project.version}.jar" outfile="${project.build.directory}\my-application.exe"/>
                    <delete file="${project.build.directory}\my-application-${project.version}.jar"/> 
                    <delete file="config-temp.xml"/>
                </tasks>
            </configuration>
        </execution>
    </executions>
</plugin> 

Aqui, utilizei o plug-in Antrun.  Da linha 13 à linha 18 estão as configurações das tarefas Ant para execução.

A linha 13 copia o arquivo de configuração do Launch4J para um arquivo temporário, que é manipulado na sequência (linha 14). Neste arquivo, todas as sequências de caracteres 0.0.0.0 serão substituídas pelo número da versão do projeto (variável do próprio Maven: ${project.version}) e o número da build (variável do Hudson: ${BUILD_NUMBER}). Só lembrando, caso não esteja trabalhando com o Hudson, você deve remover esta última variável da composição dos números da versão do seu aplicativo.

A definição da tarefa do Launch4J é feita na linha 15. Pressupõe-se que o aplicativo esteja instalado no ambiente de execução do Maven. Aqui, a variável ${launch4j.dir} fora definida anteriormente no arquivo pom.xml do projeto com o caminho da instalação do aplicativo. Observe que tal variável aparece duas vezes no atributo 'classpath' desta linha.

Na sequência, linha 16, vem a chamada da própria tarefa definida da linha anterior. Esta tarefa demanda 2 atributos, sendo o nome do arquivo JAR e o nome do executável que será gerado. As variáveis utilizadas aqui são do próprio Maven.

As linhas 17 e 18 fazem uma limpeza: remove o arquivo JAR, que agora já está encapsulado como um arquivo executável, e depois remove o temporário criado na linha 13.

Conclusão

A solução apresentada para se trabalhar com Launch4J e Maven atende aqueles que estão começando a trabalhar tanto com um ou outro destes aplicativos, sem exigir muita configuração por parte Maven. O plugin Antrun utilizado para fazer toda a configuração é da Apache e portanto se espera uma compatibilidade com as futuras versões do próprio Maven, facilitando assim uma possível atualização de versão.

Referências

http://launch4j.sourceforge.net/
http://ant.apache.org/
http://9stmaryrd.com/tools/launch4j-maven-plugin/
http://mvnrepository.com/artifact/com.akathist.maven.plugins.launch4j/launch4j-maven-plugin
http://stackoverflow.com/questions/6060560/trying-to-integrate-launch4j-in-a-maven-project-using-alakai-plugin

Jogo da Memória em JavaFX 2.1

Aqui, disponibilizo o código fonte de um jogo da memória feito em JavaFX 2.1. O código foi desenvolvido pelo NetBeans 7.1.1. O arquivo compactado contém todo o código fonte, arquivos e áudio e um arquivo JAR que consiste no jogo pronto para ser executado. Ao clicar no botão ou no link para download, você será direcionado para o Google Drive e todos os arquivos do projeto serão listados. Para fazer efetivamente o download, tecle CTRL+S ou vá no menu 'File' e selecione a opção 'Download' para fazer um único download de todos os arquivos.

Vale lembrar que para compilar o jogo, você precisa do JDK 6 ou superior com o módulo para JavaFX, que pode ser baixado pelo site da Oracle. Se você possui o JDK 7 atualizado, então o JavaFX já está incorporado.

No código deste jogo, vocês verão a criação de formas, aplicação de transformações e efeitos, animação sincronizada com áudio e interação com usuário. Tudo isso já foi descrito nos artigos anteriores, de forma didática.

Este jogo da memória é baseado naquele famoso jogo eletrônico da década de 80 chamado Genius. Nesta implementação, a lógica basicamente é a repetição pelo usuário de uma sequência determinada pelo jogo. A sequência pode ser mais rápida ou mais lenta, até atingir uma sequência de 10, começando sempre com 4.

Durante o jogo, é sempre informado a sequência criada pelo sistema e quando o usuário deve iniciar sua interação. As mensagens são sempre exibidas no centro do círculo e desaparecem após 3ms com efeito de 'fading'.

Ao iniciar o jogo, o botão fica desabilitados até a sua finalização. Todavia, o controle para modificar a velocidade do jogo continua habilitado.

A sequência criada pelo jogo é apresentada ao usuário pela animação de cada quarto de círculo, causando a sensação de que fora ligado/aceso e depois apagado. Aqui foi usado o RadialGradient para produzir o efeito desejado, ou seja, para que mais ou menos no centro de cada forma fosse mais claro e no restante mais escuro. Ainda, para cada cor do círculo existe um som associado, executado de forma síncrona com a animação.

Durante a exibição automática da sequência criada pelo jogo, o usuário não tem como interagir com o sistema (exceto mudando a velocidade do jogo).

Após ser informado pelo jogo, com a mensagem 'Sua vez!', o usuário pode então tentar repetir a sequência criada. Não foi criado um tempo limite (timeout) para início da interação do usuário.

Caso o usuário erre a sequência exibida pelo jogo, um som, uma mensagem e efeito de 'fading' do círculo e da mensagem são executados paralelamente. Após 3ms, o jogo volta ao estado inicial, habilitando o botão inciar, pronto para um novo jogo.

Se o usuário conseguir repetir a sequência de 10, sem errar, o jogo também emite som, exibe mensagem e um efeito de rotação de 360 graus horário. Da mesma forma, após 3ms, o jogo volta ao estado inicial, habilitando o botão inciar, pronto para um novo jogo.


Para finalizar, não posso deixar de referenciar o artigo de João Paulo chamado "Genius em C#". Neste artigo, o autor também disponibiliza o código-fonte do seu jogo, junto com arquivos de áudio. Estes mesmos arquivos de áudio são utilizados neste exemplo em JavaFX.



quinta-feira, 28 de junho de 2012

Formas, Transformações e Efeitos em JavaFX 2.1

Brincando com JavaFX, versão 2.1, desenvolvi um jogo de memória para exercitar diversos conceitos do framework. Neste artigo, não descrevo exatamente o desenvolvimento do jogo, mas simplesmente a criação da forma básica, a aplicação de transformações nesta forma para se obter o resultado final e ainda a aplicação de efeito de iluminação para criar a ilusão de volume.

Somente para contextualizar,  jogo em questão é uma tentativa de reproduzir (aqui neste artigo, visualmente pelo menos) aquele jogo eletrônico chamado de Genius, famoso na década de 80. Veja na Wikipedia mais sobre esse jogo. Ainda, em vários outros sites é possível encontrar várias versões online para jogar.

Formas


O jogo tem a forma de uma rosquinha de quarto partes coloridas. Cada parte vou chamar de um quarto de círculo.

Produzir a forma da figura ao lado no JavaFX é muito simples: basta criar dois círculos com raios diferentes e integrar estas duas formas para produzir a forma de uma rosquinha. A partir desta forma, agora, deve-se executar a intersecção com um retângulo cujos lados devem ter valor igual ao maior raio dos círculos criados anteriormente (então estamos falando de um quadrado, na verdade).

O código abaixo traduz tudo o que foi escrito acima. Ler o código é mais fácil que ler qualquer outra explicação.
Circle c1 = new Circle(100);
Circle c2 = new Circle(50);
Shape donut = Shape.subtract(c1, c2);
Rectangle r1 = new Rectangle(100, 100);
Shape quarter = Shape.intersect(donut, r1);

Efeito de Iluminação


Adicionar uma cor à forma criada não bastaria para deixá-la mais interessante visualmente. Além da cor, um efeito de luz é necessário para dar à forma um efeito tridimensional, como mostra a figura ao lado.

Em JavaFX, Light representa um tipo/fonte de luz enquanto Lighting representa o efeito desta luz sobre uma forma.

Abaixo, segue o código, com comentários,  para produzir o efeito apresentadona figura ao lado.




// Cria-se uma luz
Light.Distant light = new Light.Distant();
light.setAzimuth(-135.0);
light.setElevation(60);
// Com esta fonte de luz, cria-se a iluminação
Lighting lighting = new Lighting(light);
lighting.setSurfaceScale(2.0);
// A cor e iluminação é aribuída à forma
quarter.setFill(Color.BLUE);        
quarter.setEffect(lighting);

Rotação & Translação


Para completar o círculo, primeiramente deve-se criar 4 vezes a forma de um quarto de círculo, atribuir uma cor distinta e o mesmo efeito de luz e iluminação às formas. Depois, é necessário rotacionar as formas para obter cada quarto de círculo na devida posição para completar o círculo, ou seja, nas posições noroeste, nordeste, sudoeste e sudeste. Como a primeira forma já ocupa a posição sudeste, para obter as outras posições deve-se rotacionar as demais formas em 90, 180 e 270.

A rotação é suficiente para montar o círculo desejado para o jogo, mas sua exibição ainda não é adequada pois não está centralizada. Para conseguir este resultado, cada forma tem que ser transladada em +/- 50 pixels, de acordo com sua rotação.

O código abaixo mostra o que foi descrito acima. Vale lembrar que as variáveis donut, r1 e lighting já foram instanciadas e descritas anteriormente.

// Quatro formas iguais
Shape quarter1 = Shape.intersect(donut, r1);
Shape quarter2 = Shape.intersect(donut, r1);
Shape quarter3 = Shape.intersect(donut, r1);
Shape quarter4 = Shape.intersect(donut, r1);        
// Posição NOROESTE
quarter1.setEffect(lighting);
quarter1.setFill(Color.BLUE);
quarter1.getTransforms().add(new Rotate(180));
quarter1.getTransforms().add(new Translate(-50,-50));
// Posição NORDESTE
quarter2.setEffect(lighting);
quarter2.setFill(Color.RED);
quarter2.getTransforms().add(new Rotate(270));
quarter2.getTransforms().add(new Translate(-50,50));
// Posição SUDESTE - não precisa rotacionar
quarter3.setEffect(lighting);
quarter3.setFill(Color.YELLOW);
quarter3.getTransforms().add(new Translate(50,50));
// Posição SUDOESTE
quarter4.setEffect(lighting);
quarter4.setFill(Color.GREEN);
quarter4.getTransforms().add(new Rotate(90));
quarter4.getTransforms().add(new Translate(50,-50));

O layout StackPane é bem adequado para exibir as formas criadas, pois exibe cada uma sobreposta e, como a rotação já foi realizada, elas serão serão adequadamente sobrepostas e formarão o círculo esperado.

public void start(Stage primaryStage) {
    /**
     * Todo código anterior ...
     */
    StackPane root = new StackPane();
    root.getChildren().add(quarter1);
    root.getChildren().add(quarter2);
    root.getChildren().add(quarter3);
    root.getChildren().add(quarter4);
    primaryStage.setScene(new Scene(root, 300, 250));
    primaryStage.show();
}

Ao lado, segue a imagem com o resultado final: um círculo formado de quatro partes com um certo volume aparentando uma forma tridimensional.

Ainda falta criar muita coisa para um jogo, mas  o mais importante é também permitir a interação do usuário com cada forma separadamente. Em outras palavras, cada quarto do círculo precisa estar pronto para receber eventos de mouse e executar uma animação. Esta animação viria a ser a sensação de acender rapidamente e apagar mais suavemente cada quarto de círculo.





Interação com Usuário


Cada quarto de círculo de capturar eventos de mouse quando o usuário clicar sobre a forma. Para tanto, precisamos definir uma classe que implemente a interface EventHandler<T extends Event>. note que esta interface pede a definição de um tipo genérico, que neste caso deve ser qualquer classe que implemente a interface Event. Como a intenção é capturar eventos de mouse, então esta classe é a MouseEvent. O código abaixo demonstra a criação e instanciação de uma classe anônima que implementa a interface requerida para a captura de eventos de mouse.

EventHandler<MouseEvent> mouseEvent = new EventHandler<MouseEvent>(){

    @Override
    public void handle(MouseEvent arg0) 
        Paint color = ((Shape)arg0.getSource()).getFill();
        System.out.println("cor: " + color.toString());
    }
};

Observe que, uma vez o evento capturado, a origem do evento é recuperada e, a partir de um conversão explícita para Shape, pega-se a cor da forma. Então é gerada uma saída no console com o valor, em hexadecimal, da cor.

Até aqui, simplesmente foi definida e instanciada uma classe anônima para tratar eventos de mouse. Agora, precisa fazer com que cada forma trate o evento de clique de mouse com esta instância. O código abaixo mostra o que deve ser feito:

quarter1.setOnMouseClicked(mouseEvent);
quarter2.setOnMouseClicked(mouseEvent);
quarter3.setOnMouseClicked(mouseEvent);
quarter4.setOnMouseClicked(mouseEvent);

Neste exemplo, pela interface gráfica o usuário não tem ainda nenhuma percepção que o quarto de círculo recebeu o evento do mouse. O ideal é que seja executada uma animação.

Animação & Mídia


A animação deve ser aquela que dê a sensação para o usuário de que a forma clicada acenda repentinamente e apague gradualmente durante um certo período e tempo. Ainda, a execução de áudio durante a animação torna a interação com o usuário mais interessante.

Veja aqui, neste meu outro artigo chamado "JavaFX Animation and Media Synchronization", como criar uma animação simples e fazer a sincronização com o áudio.

JavaFX Animation and Media Synchronization

I've been experiencing JavaFX 2.1 lately and I've created pieces of code to work some concepts out. One of those is how to synchronize animation and media (audio, more precisely). My fisrt shot was to create instances of a Timeline, a MediaPlayer and then invoke the play() method of both instances sequentially. That solution seems to work rather nice since you have just one anination and one audio to play.  Although, what if you have a sequence of anination and audio to play?

An instance of SequentialTransition fits perfectly if you want to play a row of aninations, one right after the other. And an instance of ParallelTransition plays animations at the same time, or at least concurrently. To get things more interesting, you can create instances of ParallelTransition and add them to an instance of SequentialTransition, or the other way around. Both types accepts instances of any type that extends Animation, which is an abstract class of JavaFX. Timeline, SequentialTransition and ParallelTransition themselves, and other transitions all extend Animation, but MediaPlayer don't! So I had to figure out how to synchronize animation and media simultaneously and then play them in a sequence of animations and medias.

Animation

First, let's create a simple animation using Timeline that changes colors of a certain shape. The execution  of such animation takes 5 seconds and color changes smoothly. The piece of code below just creates a rectangle and other instances to represent each color using the RGB color system (Red, Green and Blue). An anonimous class is also created and instanciated which implements the ChangeListener interface; here is where the color of the shape changed. Then, the listener instance is added to each color variable instance.

final Rectangle anyShape = new Rectangle(250, 200);
               
final DoubleProperty redColor = new SimpleDoubleProperty();
final DoubleProperty greenColor = new SimpleDoubleProperty();
final DoubleProperty blueColor = new SimpleDoubleProperty();
        
ChangeListener colorListener = new ChangeListener() {

    @Override
    public void changed(ObservableValue arg0, Object arg1, Object arg2) {
        anyShape.setFill(Color.color(redColor.doubleValue(), greenColor.doubleValue(), blueColor.doubleValue()));  
    }
};
        
redColor.addListener(colorListener);
greenColor.addListener(colorListener);
blueColor.addListener(colorListener);

Now, the color variable instances have to change and that's the animation we're talking about. Here, the Timeline comes into play.

Two colors is picked up to make the transition. Then, an instance of Timeline is created and a sequence of KeyFrame instances is added to it. Pay attention to the pairs of KeyFrame instances: each pair handle a color within a period of time, i.e., Duration instances. Besides Duration instance, the KeyFrame contructor demands a KeyValue instance as well. That one takes in a color variable instance and a start color. So, one KeyFrame instance defines that a variable is set to a certain value at a certain period of time and along another KeyFrame instance, such value is interpolated to another one until another period of time.

Color startColor = Color.BLUEVIOLET;
Color endColor = Color.YELLOWGREEN;

Timeline animation = new Timeline();
animation.getKeyFrames().addAll(new KeyFrame(new Duration(0.0), new KeyValue(redColor, startColor.getRed())),
                                new KeyFrame(new Duration(5000.0), new KeyValue(redColor, endColor.getRed())),
                                new KeyFrame(new Duration(0.0), new KeyValue(greenColor, startColor.getGreen())),
                                new KeyFrame(new Duration(5000.0), new KeyValue(greenColor, endColor.getGreen())),
                                new KeyFrame(new Duration(0.0), new KeyValue(blueColor, startColor.getBlue())),
                                new KeyFrame(new Duration(5000.0), new KeyValue(blueColor, endColor.getBlue())));

animation.play();

Audio

Time to load an audio file and make it play too. The piece of code below does so. The audio file here is inside the JAR file. First, the URL to the audio file is retrieved and then the toExternalForm() method is invoked to get the real path, otherwise, it will not work when the application is executed by the JAR file itself (it will work only within the development IDE).

URL soundURL = this.getClass().getResource("/resource/audio.wav");
final MediaPlayer mediaPlayer = new MediaPlayer(new Media(soundURL.toExternalForm()));
mediaPlayer.play();

Synchronizing Animation and Audio

Since ParallelTransition does not accept a MediaPlayer instance (which makes sense), animation and media synchronization has to have its own way. Actually, Timeline works it out by itself, along KeyFrame and EventHandler instances. The KeyFrame constructor, besides a Duration instance, also accepts an instance of EventHandler. So, at start time, an EventHandler instance must be set, as well as at stop time. The piece of code below shows how to create instances of EventHandler as anonymous classes. Next, KeyFrame instances are created which take in the EventHandler instances at start and stop durations and are right away added to the animation, i.e., the Timeline instance.

EventHandler<ActionEvent> startAudio = new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent arg0) {
        mediaPlayer.play();
    }
};

EventHandler<ActionEvent> stopAudio = new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent arg0) {
        mediaPlayer.stop();
    }
};

animation.getKeyFrames().add(new KeyFrame(new Duration(0.0), startAudio));
animation.getKeyFrames().add(new KeyFrame(new Duration(5000.0), stopAudio));

animation.play();

Conclusion

The main intention of this post is to show how to synchronize animation and media in JavaFX 2.1, using Timeline, KeyFrame and EventHandler classes. Alongside, a simple animation and the load of an audio file are also described and used as basis to the main subject.

sexta-feira, 25 de maio de 2012

Hudson on Tomcat: Subversion Plugin Fails to Connect Behind Proxy

I bumped into an annoying problem working with Hudson 2.2.0 on Tomcat 7.0: Subversion plugin could not connect to SVN server because proxy server required authentication. See below a short version of the error message displayed by Hudson:

FAILED: org.tmatesoft.svn.core.SVNErrorMessage: svn: OPTIONS /svn/system/trunk/app failed
org.tmatesoft.svn.core.SVNException: svn: OPTIONS /svn/system/trunk/app failed
 at org.tmatesoft.svn.core.internal.io.dav.http.HTTPConnection.request(HTTPConnection.java:294)
... 

Hudson offers a way to enter proxy settings but they have no effect on Subversion plugin. Anyway, I didn't want Subversion plugin to use proxy because my SVN server was on the same local network. So, where to change proxy setting for Subversion plugin on Hudson?

After days trying to make it work, I came up to a workaround to that problem. Though it's the weirdest solution ever, it solved my problem. On Tomcat, I also had Nexus 2.0.4 running. Using the trial and error method, I realised that right after entering proxy settings on Nexus (yes, I mean right on Nexus), Subversion plugin on Hudson failed to connect to the SVN server. Although proxy settings on Nexus were all right, I hadn't  included the SVN server as a non proxy host (of course not ... why should I have to on Nexus?). After entering the SVN server address as a non proxy host on Nexus, the Subversion plugin on Hubson worked right away.

Somebody might say that it had to do with JVM proxy settings, but it seemed to be set properly though.

Later, I had to install the same softwares, including same versions, on another machine. And guess what? Everything worked fine the way it should be. So I had the opportunity to compare Tomcat, JVM, Nexus, Hudson and OS settings but all proxy settings were the same.

I am still clueless.


quinta-feira, 8 de março de 2012

Como Limpar um InputText no JSF a Partir do Managed Bean (Controller)

O objetivo aqui é simplesmente criar um formulário para inserir itens numa lista e atualizar os componentes automaticamente, sem ter que carregar toda a página. Em outras palavras, ao pressionar o botão para fazer a inserção, é lista é atualizada com o novo item e o campo de entrada do título item é limpo. Para tanto, utilizei JSF 2 e Primefaces 3.1. A solução foi encontrada no fórum Code Ranch, onde dois membros postaram duas soluções (Prakash Jebaraj & Gregg Bolinger) diferentes.

Solução 1
O código abaixo foi extraído dos meus testes e usei componentes do Primefaces. Aqui, somente uma entrada de texto e um botão para inserir fazem parte do foumulário para exemplificação.

<h:form id="idForm">
   <p:inputtext id="idNome" value="#{myManagedBean.nome}"/>
   <p:commandbutton actionlistener="#{myManagedBean.inserirNome(event)}" immediate="true" update="idNome" value="Inserir"/>
</h:form>

Ao pressionar o botão, o método inserirNome do meu Managed Bean é chamado, a página não é recarregada e o campo de entrada de texto é limpo.

@ManagedBean(name = "myManagedBean")
@SessionScoped
public class ManagedBean {
    
    private String nome;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome= nome;
    }

    public void inserirNome(ActionEvent event) {
         // TODO: inserir nome em alguma lista ou base de dados
        FacesContext facesContext = FacesContext.getCurrentInstance();
        UIViewRoot uiViewRoot = facesContext.getViewRoot();
        HtmlInputText inputText = (HtmlInputText) uiViewRoot.findComponent("idForm:idNome");
        inputText.setSubmittedValue("");
        this.setNome("");
    }
}

Note que na linha 19 do código acima, a string usada para localizar o HtmlInputText é a concatenação dos identificadores da árvore de componentes da página, separado por dois pontos. Assim,  qualquer alteração nos identificadores ou na hierarquida dos componentes da página vai diretamente afetar esta solução.

Solução 2
A segunda solução não é muito diferente da primeira. Aqui, a alteração dos identificadores de componentes da página não tem impacto na solução porque o campo de entrada de texto é explicitamente associado a um elemento de interface gráfica do lado o servidor. Veja abaixo, no componente
inputText, a propriedade binding em uso, onde é feita esta associação.


<h:form id="idForm">
   <p:inputtext id="idNome" value="#{myManagedBean.nome}" binding="#{myManagedBean.uiNome}"/>
   <p:commandbutton actionlistener="#{myManagedBean.inserirNome(event)}" immediate="true" update="idNome" value="Inserir"/>
</h:form>

Desta forma, o Managed Bean precisa ter um atributo do tipo UIInput, com seus métodos get e set. Já o método inserirNome,invocado pelo actionListener do botão de inserir, tem menos código para fazer a limpeza do campo de entrada.

@ManagedBean(name = "myManagedBean")
@SessionScoped
public class ManagedBean {
    
    private String nome;
    private UIInput uiNome;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome= nome;
    }

    public String getUiNome() {
        return uiNome;
    }

    public void setUiNome(String uiNome) {
        this.uiNome= uiNome;
    }

    public void inserirNome(ActionEvent event) {
        // TODO: inserir nome em alguma lista ou base de dados        
        this.getUiNome().setSubmittedValue("");
        this.setNome(""); 
    }
}

Esta segunda solução não está diretamente associada ao identificadores dos elementos da interface gráfica, assim, qualquer alteração de identificadores de componentes da página não vai afetar o código.

Referências:
http://www.coderanch.com/t/210589/JSF/java/Clearing-forms
https://cwiki.apache.org/confluence/display/MYFACES/Clear+Input+Components
http://www.guj.com.br/java/92758-resolvidolimpar-o-resultado-da-consulta-e-os-campos-do-formulario-com-jsf

terça-feira, 14 de fevereiro de 2012

Nova Senha de Root no MySQL Para Linux

Peguei uma instalação do CentOS 5.5 com MySQL 5.0 e não sabia a senha do usuário root do MySQL. Então precisava definir uma nova senha para o usuário root, mesmo não conhcendo a senha atual.

1 - Parar o Servidor MySQL
Pare o servidor MySQL em primeiro lugar. Para isso, você terá que entrar com a senha de super usuário para poder executar o comando.

su
/etc/init.d/mysqld stop

2 - Reiniciar o Servidor MySQL em Modo de Segurança
O servidor MySQL deve ser reiniciado em modo de segurança com alguns parâmetros de incialização para permitir modificações, como usuário local apenas, na tabela de usuário.

/usr/bin/mysqld_safe --skip-grant-tables --skip-networking &

ATENÇÃO: a visualização do código acima está prejudicada pela formatação! Veja que existe o caractere 'underscore' entre mysqld e safe, sendo portanto um único comando e não dois como parece à primeira vista.

3 - Alterar a Senha do Usuário Root
Registre-se agora no MySQL para executar o comando SQL para alterar a tabela de usuário com uma nova senha para o root:

mysql
update mysql.user set password=PASSWORD('novasenha') where user='root';
quit;

4 - Reiniciar o Servidor MySQL
O servidor MySQL deve agora ser reiniciado em modo normal.

/etc/init.d/mysqld stop
/etc/init.d/mysqld start

5 - Testar a Nova Senha
Teste agora a nova senha do usuário root no MySQL.

mysql -u root -p

Para mais informações, consulte a referência principal utilizada neste texto.

Referências:

Instalando VirtualBox Guest Additions no CentOS

Na minha VirtualBox, versão 4.1.0, tenho instalado o CentOS 5.5. O VirtualBox Guest Additions é um conjunto de drivers instalado no próprio sistema operacional virtualizado para otimizar a performance e usabilidade.

Antes da instalação propriamente dita, é preciso se certifiar que o kernel esteja atualizado e alguns pacotes instalados no CentOS.

1 - Atualização do Kernel
Abra o terminal, registre-se como usuário root e execute a atualização do kernel:

su -
yum update kernel*

Após a atualização, reinicie o sistema operacional.

2 - Instalação de Pacotes
Volte ao terminal como usuário root.

su -

Agora verifique se os pacotes gcc, kernel-devel e kernel-headers estão instalados no sistema.

rpm -q gcc
rpm -q kernel-devel
rpm -q kernel-headers

Se algum dos pacotes não estiver instalado, execute a instalação.

yum install gcc
yum install kernel-devel
yum install kernel-headers

Novamente reinicie a o sistema operacional.

3 - Instalação do VirtualBox Guest Additons
Conforme mostra a figura abaixo, monte o CD do VirtualBox Guest Additions. Note que o menu acessado não é o menu do CentOS, mas sim o menu da máquina virtual.


Abra novamente o terminal com usuário root, vá para o diretório montado do VirtualBox GuestAdditions e execute a instalação.

su -
cd /media/VBOXADDITIONS_4_1.0_73009
sh VBoxLinuxAdditions.run

Note que no exemplo acima, o nome usado para acessar o CD montado corresponde a versão do VirtualBox da minha instalação.

Após a instalação, reinicie novamente a máquina.

4 - Referências