com o IDE NetBeans Lucio - Projetos

Propaganda
UNIVERSIDADE FEDERAL DE SANTA CATARINA
Integração do Sistema de Controle de Versão Subversion (SVN) com
o IDE NetBeans
Lucio Moratelli Prado
Florianópolis – SC
2006/2
UNIVERSIDADE FEDERAL DE SANTA CATARINA
DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA
CURSO DE SISTEMAS DE INFORMAÇÃO
Integração do Sistema de Controle de Versão Subversion (SVN) com
o IDE NetBeans
Lucio Moratelli Prado
Trabalho de conclusão de curso
apresentado como parte dos requisitos para
obtenção do grau de Bacharel em Sistemas de
Informação.
Florianópolis – SC
2006/2
2
Resumo
A qualidade de software é um dos assuntos mais atuais em discussão na
comunidade de Engenharia de Software. Embora os primeiros esforços mais
amplos no sentido de se produzir software com maior qualidade e produtividade
datem da década de 70, foi na segunda metade da década de 90 que uma série
de novos conceitos e abordagens alcançaram maturidade e visibilidade.
Devido ao grande número de elementos, ou itens de configuração,
geralmente envolvidos num desenvolvimento de software, torna-se essencial a
utilização de ferramentas que auxiliem na realização das diversas tarefas
envolvidas no processo de desenvolvimento de software. Os Ambientes
Integrados de Desenvolvimento (IDE – Integrated Development Environment)
surgiram para apoiar esse processo através da integração dessas diversas
ferramentas.
Dentre as ferramentas destacam-se as de controle de versão, cujos
objetivos são: armazenar os itens que são produzidos no desenvolvimento,
permitir o acesso de maneira controlada a quaisquer versões destes elementos,
armazenar informações de histórico dos itens de forma a estabelecer a base para
o controle da evolução do produto de software, e reter informações que permitam
automatizar o acesso aos conjuntos de itens de configuração que compõem o
produto de software num específico estado de seu desenvolvimento.
Este trabalho apresenta uma proposta de um plug-in que implemente a
integração entre um dos ambientes integrados de desenvolvimento mais utilizados
para desenvolvimento de software JAVA, o NetBeans, com uma ferramenta de
3
controle de versão que vem ganhando espaço na comunidade open source, o
Subversion.
Palavras-Chaves:
NetBeans,
Subversion,
ambiente
integrado
de
desenvolvimento de software, gerência de configuração, controle de versão, plugin.
4
Abstract
The software quality is one of the most recent subjects in the Software
Engineering community discussion. Although the first ampler efforts in the direction
of producing software with bigger quality and productivity date on 70 decade, was
in the second half of 90 decade that a series of new concepts and boardings had
reached maturity and visibility.
Had the great number of elements, or configuration items, generally involved
in a software development, the tools utilization becomes essential that assist in the
accomplishment of the diverse involved tasks in the development software
process. The Integrated Development Environments (IDE) had appeared to
support this process through the tools integration.
Amongst the tools they are distinguished of version control, whose
objectives are: to store items that they are produced in the development, to allow
the access in controlled way to any versions of these elements, to store information
of items description to establish the base for the evolution control of software’s
product, and to hold back information that allow to automatize the access to sets of
configuration items that compose the software’s product in a specific development
state.
This work presents one proposal plug-in that implements the integration
between one of most used IDEs for software development JAVA, the NetBeans,
with a tool of version control that comes earning space in the open source
community, the Subversion.
5
Keywords: IDE, SVN, NetBeans, Software Configuration Management,
Version Control, plug-in, integrated development environment.
6
Sumário
Resumo .................................................................................................................. 3
Abstract.................................................................................................................. 5
Lista de Figuras................................................................................................... 10
Lista de Tabelas .................................................................................................. 13
Lista de Abreviaturas .......................................................................................... 14
1 Introdução......................................................................................................... 15
1.1 Motivação .................................................................................................... 16
1.2 Objetivos...................................................................................................... 18
1.2.1 Objetivo Geral ....................................................................................... 18
1.2.2 Objetivo Específico................................................................................ 18
1.2.3 Escopo de Estudo ................................................................................. 19
2 O Processo de Desenvolvimento de Software .............................................. 20
2.1 Ambientes Automatizados de Apoio a Gerência do Processo de Software. 23
2.1.1 Ambientes de Desenvolvimento de Software (ADS) ............................. 24
2.1.1.1 Integração de Ferramentas ............................................................ 26
2.1.1.2 Arquitetura ...................................................................................... 29
2.1.2 Software Configuration Management (SCM)......................................... 32
2.1.2.1 Sistema de Controle de Versão (VCS - Version Control System)... 35
2.1.2.1.1 Termos ..................................................................................... 36
2.1.2.1.2 Atividades Comuns .................................................................. 37
2.1.2.1.3 Algumas Ferramentas de Controle de Versão Existentes........ 38
7
2.1.2.1.4 Comparação entre o CVS e o SVN.......................................... 42
3 O Ambiente NetBeans...................................................................................... 46
3.1 A Arquitetura do IDE NetBeans ................................................................... 47
3.2 O Desenvolvimento de Plug-ins no IDE NetBeans ...................................... 48
3.2.1 As APIs Abertas (Open APIs) ............................................................... 49
3.2.2 Modularidade ........................................................................................ 50
3.2.3 Hierarquia, Arquivos e Nós ................................................................... 51
3.2.4 A Abstração de Arquivos no NetBeans ................................................. 52
3.2.5 Mapeando Arquivos Para Objetos......................................................... 54
3.2.6 O Sistema de Arquivos do NetBeans .................................................... 55
3.2.7 Camadas dos Módulos.......................................................................... 56
3.2.8 Camadas no Sistema de Arquivos ........................................................ 58
4 O Plug-in SVN Proposto .................................................................................. 60
4.1 O Processo de Desenvolvimento do Plug-in Proposto ................................ 72
4.1.1 A Biblioteca Java SVN .......................................................................... 76
4.1.2 Primeiro Ciclo de Desenvolvimento, o Caso de Uso Checkout............. 76
4.1.2.1 A Fase de Análise........................................................................... 77
4.1.2.2 A Fase de Projeto ........................................................................... 79
4.1.2.3 A Fase de Implementação.............................................................. 87
4.1.3 Segundo Ciclo de Desenvolvimento, o Caso de Uso Commit............... 93
4.1.3.1 A Fase de Análise........................................................................... 93
4.1.3.2 A Fase de Projeto ........................................................................... 95
4.1.3.3 A Fase de Implementação.............................................................. 97
4.1.4 Terceiro Ciclo de Desenvolvimento, o Caso de Uso Update................. 98
8
4.1.4.1 A Fase de Análise........................................................................... 99
4.1.4.2 A Fase de Projeto ........................................................................... 99
4.1.4.3 A Fase de Implementação............................................................ 100
5 Conclusões ..................................................................................................... 101
5.1 Sugestões para Trabalhos Futuros............................................................ 102
6 Referências Bibliográficas ............................................................................ 103
Anexo 1 – Artigo................................................................................................ 107
Anexo 2 – Artefatos UML .................................................................................. 113
Anexo 3 – Código Fonte ................................................................................... 124
9
Lista de Figuras
Figura 1 - Elementos envolvidos em atividades do processo de software [LIM 98].
.............................................................................................................................. 21
Figura 2 - Níveis de integração de ferramentas em um ADS [BRO 92]. ............... 28
Figura 3 - Modelo de Referência ECMA [BRO 92a] .............................................. 30
Figura 4 – MultiFileSystem [GRE 2002]. ............................................................... 53
Figura 5 - Adicionando um .instance para o Sistema de Arquivos [GRE 2002]. ... 55
Figura 6 - Módulo adicionando uma camada XML no sistema de arquivos [GRE
2002]. .................................................................................................................... 57
Figura 7 - Estrutura do sistema de arquivos [GRE 2002]. ..................................... 59
Figura 8 - Localização do CVS na barra de menus do NetBeans. ........................ 60
Figura 9 - Localização do SVN na barra de menus do NetBeans. ........................ 61
Figura 10 - Ícones ilustrando as operações SVN. ................................................. 61
Figura 11 - Primeira tela do Wizard de Checkout.................................................. 62
Figura 12 - O preenchimento dos campos obrigatórios habilita o botão Next. ...... 63
Figura 13 - Barra de progresso indicando a tarefa de conexão em curso............. 63
Figura 14 - Exemplo de mensagem de erro na tentativa de conexão. .................. 64
Figura 15 - Segunda tela do Wizard de Checkout................................................. 64
Figura 16 - Botões para acesso as janelas de auxílio no Wizard de Checkout. .... 65
Figura 17 - Janela de exploração do repositório. .................................................. 66
Figura 18 - Janela de escolha do diretório a ser feito o checkout. ........................ 67
Figura 19 - Janela de pesquisa das versões de um artefato................................. 68
10
Figura 20 - Barra de progresso indicando o andamento da tarefa de checkout.... 69
Figura 21 - Exemplo de erro ao executar a operação de checkout. ...................... 69
Figura 22 - O plug-in oferece a possibilidade de abrir um projeto recebido do
repositíorio............................................................................................................. 70
Figura 23 - O plug-in fornece a possibilidade de criar um projeto a partir dos dados
do checkout. .......................................................................................................... 70
Figura 24 - Janela com os artefatos para commit. ................................................ 71
Figura 25 - Janela de log das operações. ............................................................. 72
Figura 26 - Diagrama de casos de uso. ................................................................ 73
Figura 27 – Rascunho inicial do modelo conceitual. ............................................. 74
Figura 28 - Diagrama de seqüência do caso de uso checkout.............................. 78
Figura 29 - Modelo conceitual expandido.............................................................. 79
Figura 30 - Solicitando a realização do checkout. ................................................. 80
Figura 31 - Janela 1. ............................................................................................. 81
Figura 32 - Janela 2. ............................................................................................. 81
Figura 33 - Diagrama de colaboração de registrarRepositório. ............................. 84
Figura 34- Diagrama de colaboração de registrarArtefato. ................................... 84
Figura 35 - Diagrama de colaboração de registrarCaminho.................................. 84
Figura 36 - Diagrama de colaboração de terminarOperação. ............................... 84
Figura 37 - Diagrama de Classes de Projeto......................................................... 86
Figura 38 - Wizard de criação de projetos do NetBeans. ...................................... 88
Figura 39 - Menu de projetos no NetBeans........................................................... 90
Figura 40 - Wizard de criação de novos arquivos. ................................................ 91
Figura 41 - Um trecho do arquivo XML Layer criado............................................. 91
11
Figura 42 - Janela da operação de commit. .......................................................... 96
Figura 43 - Diagrama de colaboração de adicinarArtefato. ................................... 97
Figura 44 - Diagrama de colaboração de realizarOperação................................ 100
12
Lista de Tabelas
Tabela 1 - Comparação entre as operações no repositório. ................................. 44
Tabela 2 - Comparação entre as características operacionais dos sistemas........ 44
Tabela 3 - Comparação entre as características gerais. ....................................... 45
Tabela 4 - Comparação entre as interfaces dos sistemas. ................................... 45
Tabela 5 - Comparação entre as formas de licenciamento dos sistemas. ............ 45
13
Lista de Abreviaturas
IDE – Integrated Development Environment
OSSD – Open Source Software Development
CVS – Concurrent Versions System
SVN – Subversion System
ADS – Ambiente de Desenvolvimento de Software
CASE – Computer-Aided Software Engineering
I-CASE – Integrated CASE
ECMA – European Computer Manufacturers Association
SCM – Software Configuration Management
SEI – Software Engineering Institute
SCI – Software Configuration Item
VCS – Version Control System
RCS – Revision Control System
CM – Configuration Management
JDK – Java Developers Kit
XML – Extensible Markup Language
API – Application Programming Interface
SPI – Service Provider Interface
JAR – JAVA Archiver
14
1 Introdução
O desenvolvimento da qualidade de software é uma tarefa complexa e
trabalhosa. Sistemas de software tendem a se tornarem cada vez maiores e a
complexidade de desenvolvimento e manutenção se torna mais difícil. Os
softwares de controle de tráfico aéreo são algumas das coisas mais complexas já
construídas pelo homem. Eles são o resultado da cooperação de muitas pessoas
que tem trabalhado juntas como um grande time. Cada pessoa, ou grupo,
gerenciando somente uma pequena parte de todo o sistema. Métodos e
mecanismos são necessários para que seja possível dividir um grande sistema em
partes gerenciáveis e, o mais importante, juntar essas partes novamente para dar
forma ao sistema completo novamente.
Podemos citar, como outro exemplo de aplicação complexa, os sistemas de
comutação telefônica. O que torna esses sistemas ainda mais complexos é o fato
de que eles são desenvolvidos em muitas versões e variantes diferentes. O
desenvolvimento dessas versões e variantes tem que ser feito de forma que todas
elas sejam mantidas para o caso de haver necessidade de restaurá-las. Além
disso, como cada parte do sistema pode existir em diversas versões, nós
precisamos ter caminhos para controlar, armazenar e recuperar exatamente qual
versão de cada parte que entrou no produto final [LARS 95].
Esta explosão na complexidade dos sistemas tornou necessária a
automatização do processo de desenvolvimento para garantir o controle e a
gerência efetivos do processo de software [LIM 98]. Em resposta a essa
necessidade surgiram ferramentas individuais de apoio à programação, como
15
compiladores, montadores e depuradores e, em seguida, ferramentas de apoio a
outras fases do desenvolvimento de software, como análise e projeto.
Dentre essas ferramentas, destacam-se os sistemas de controle de versão.
Muitos sistemas de controle de versão formam desenvolvidos para gerenciar o
histórico das versões de software com o intuito de produzir software de maior
qualidade e suportar trabalho em equipe.
Porém, o crescimento do número de ferramentas utilizadas no processo de
software gerou um segundo problema, a constatação que de a comunicação e a
coordenação entre todas as ferramentas usadas no desenvolvimento e
manutenção de software são essenciais para a produção eficiente de software de
qualidade.
Surgiram, então, os ambientes integrados de desenvolvimento de software
(IDE), que se propõem a integrar as diversas ferramentas utilizadas no processo
de desenvolvimento de software.
Este trabalho propõe-se a integrar uma ferramenta de controle de versão de
código fonte aberto que vem ganhando espaço na comunidade desenvolvimento
de código fonte aberto (OSSD) e o IDE NetBeans.
1.1 Motivação
A NetBeans.org é atualmente uma das maiores comunidades de
desenvolvimento de software de código fonte aberto do mundo. Ela mantém o
desenvolvimento de um
estável e robusto ambiente de desenvolvimento de
software, que cada vez mais vem ganhando adeptos nas comunidades OSSD,
16
principalmente para o desenvolvimento de software JAVA para Web, devido ao
fato de ser o IDE mais completo para este fim.
Porém, diferente de seu maior concorrente, o Eclipse, o NetBeans recebe
pouco esforço no sentido de desenvolvimento de plug-ins de terceiros, o que o
torna limitado em alguns aspectos. Isto se dá porque a sua interface para
implementação de plug-ins é bem mais complexa do que a do concorrente.
O desenvolvimento colaborativo de software se tornou a maneira mais
utilizada de produzir software de qualidade [WU 2003]. Essa visão de
desenvolvimento de software necessita de ferramentas de gerência de
configuração de software (SCM) que auxiliem no processo de desenvolvimento. O
CVS foi durante muito tempo o padrão de facto para este fim, porém, esta
ferramenta não foi estruturada para acompanhar as mudanças surgidas na
complexidade,
tanto
do
software
produzido,
como
do
processo
de
desenvolvimento em si. Dentro desta perspectiva, o Subversion surge como o
substituto do CVS e avança para se tornar a ferramenta de controle de versão
padrão nas comunidades de OSSD.
Oficialmente o NetBeans oferece para os usuários apenas a opção de
utilizar o CVS como sistema de controle de versão e, nenhum esforço foi
encontrado na comunidade para possibilitar o uso do SVN neste IDE. Já os
usuários do Eclipse possuem várias opções de plug-ins para ambas as
ferramentas.
Os usuários do NetBeans deveriam ter a opção de usar uma ferramenta de
controle de versão de maior opções e qualidade e, por isso, nós acreditamos que
17
possibilitar aos usuários do NetBeans o uso da ferramenta de controle de versão
Subversion é de suma importância para a comunidade de OSSD.
1.2 Objetivos
1.2.1 Objetivo Geral
Integrar o sistema de controle de versão Subversion no IDE NetBeans.
1.2.2 Objetivo Específico
Pesquisas em integração de ferramentas tem sido um importante tópico de
estudo desde o modelo de Stoneman proposto no final dos anos 70 e resumido
por Brown [BRO 92c] em duas categorias, o nível conceitual (“o que é integração”)
e o nível mecânico (“como prover integração”). Em geral, softwares de prateleira
são chamados integrados se funcionarem coerentemente e eficientemente em um
ambiente como um todo, como é o caso de um ambiente integrado de
desenvolvimento de software (IDE) [KAP 2005]. Neste contexto, integrar uma
ferramenta a um IDE significa desenvolver um plug-in que disponibilize as
funcionalidades dessa ferramenta.
Assim, o objetivo deste trabalho é desenvolver um plug-in para o NetBeans
que disponibilize as funções do SVN, que seja semelhante, do ponto de vista de
utilização, e compatível, com o plug-in CVS do NetBeans, que disponibilize acesso
aos repositórios através dos protocolos SVN, SVN sobre SSH e HTTP, que seja
de fácil utilização e que seja facilmente expansível para o acréscimo de novas
funcionalidades.
18
1.2.3 Escopo de Estudo
Devido ao tempo disponível para o desenvolvimento da tarefa o plug-in a
ser desenvolvido implementará as funções de checkout, commit e uptade.
19
2 O Processo de Desenvolvimento de Software
Informalmente, o processo de desenvolvimento de software (processo de
software) pode ser compreendido como o conjunto de todas as atividades
necessárias para transformar os requisitos do usuário em software [HUM 89b]
[OST 87]. Um processo de software é formado por um conjunto de passos de
processo parcialmente ordenados, relacionados com conjuntos de artefatos,
pessoas, recursos, estruturas organizacionais e restrições e tem como objetivo
produzir e manter os produtos de software finais requeridos [LON 93] [DOW 91].
Os passos de processos são atividades ou tarefas. Em [CON 94], uma
atividade é um passo de processo que produz mudanças de estado visíveis
externamente no produto de software. Atividades incorporam e implementam
procedimentos, regras e políticas, e têm como objetivo gerar ou modificar um dado
conjunto de artefatos. Elas podem ser organizadas em redes com duas dimensões
(horizontal e vertical) e estão associadas com papéis, ferramentas e artefatos.
Uma atividade aloca recursos (por exemplo, máquinas e orçamento), é
escalonada, monitorada e atribuída a desenvolvedores (agentes), que podem
utilizar ferramentas para executá-la. Uma atividade também pode ser executada
somente por ferramentas automatizadas, sem intervenção humana. Neste caso, a
atividade é automática. Toda atividade possui uma descrição, a qual pode
especificar os artefatos necessários, as relações de dependência com outras
atividades, as datas de início e fim planejadas, os recursos a serem alocados e os
agentes responsáveis pela mesma [DOW 91]. A Figura 1 ilustra os elementos
envolvidos com uma atividade de processo de software.
20
Figura 1 - Elementos envolvidos em atividades do processo de software [LIM 98].
Um agente está relacionado com as atividades de um processo e pode ser
uma pessoa ou uma ferramenta automatizada (quando a atividade é automática).
Diferentes agentes terão percepções diferentes acerca do que acontece durante o
processo de software. Um gerente, por exemplo, perceberá os aspectos de
controle e alocação de recursos e cronogramas para atividades, enquanto um
desenvolvedor perceberá as suas atividades como atribuições que devem ser
feitas para produzir um resultado [DOW 91].
Um artefato é um produto criado ou modificado durante um processo [LON
93]. Tal produto é resultado de uma atividade e pode ser utilizado posteriormente
como matéria prima para a mesma ou para outra atividade a fim de gerar novos
produtos. Desta forma, uma atividade pode consumir produtos (de entrada) e
gerar novos produtos (de saída).
21
A realização do processo é afetada pelas restrições, que podem atingir
atividades, agentes, recursos, artefatos, papéis e seus relacionamentos [LON 93].
Uma restrição é uma condição definida que um passo de processo deve satisfazer
antes ou depois de ser executado [FEI 93].
Um cargo ou papel é um conjunto de permissões e obrigações associadas
a um objetivo funcional [LON 93]. Um agente sempre ocupa um cargo quando
realiza uma tarefa. Projetista, programador e gerente são exemplos de cargos. Os
cargos podem ser organizados em estruturas tais como organogramas [DOW 91].
Um modelo de processo de software é uma descrição abstrata do processo
de software. Vários tipos de informação devem ser integrados em um modelo de
processo de software para indicar quem, quando, onde, como e por que os passos
são realizados [LON 93]. Para representar um modelo de processo de software é
utilizada uma linguagem de modelagem do processo de software, a qual deve
oferecer recursos para descrever e manipular os passos do processo.
Um modelo de processo instanciado ou processo executável é um modelo
de processo pronto para execução [FEI 93]. Este modelo pode ser interpretado por
uma máquina de processo, a qual é um componente provido por um ambiente de
desenvolvimento de software (ADS) com capacidade de suportar a modelagem e
a execução de processos de software (ADS orientado a processo).
Um projeto, segundo [CON 94], é a instância de um processo, com
objetivos e restrições específicas. Pode-se dizer que um projeto é um esforço para
desenvolver um produto de software, ou seja, envolve uma estrutura
organizacional, prazos, orçamentos, recursos e um processo de desenvolvimento.
Neste sentido, a gerência de projetos tem como responsabilidades o
22
planejamento, controle e monitoração de um processo em execução [FEI 93]
[DOW 91], enquanto a gerência de processos preocupa-se em construir, analisar e
verificar modelos de processo, para isso obtendo informações durante a execução
desse processo a fim de evoluir tais modelos para que possam ser usados
posteriormente [DOW 91].
2.1 Ambientes Automatizados de Apoio a Gerência do Processo de
Software
Para garantir o controle e a gerência efetivos do processo de software,
tornou-se necessário automatizá-lo onde possível e prover suporte às pessoas
que nele trabalham. Os ambientes que auxiliam o desenvolvimento de software
permitem esse controle e facilitam a execução das tarefas do desenvolvimento.
Existem diversos tipos de ambientes automatizados de apoio a tarefas dos
usuários. Estes ambientes estão classificados segundo [CHR 95] em: software
configuration management (SCM), produtos groupware, ADS, ADS orientados a
processo, produtos workflow e workgroup. Estas classes se diferenciam em
termos de gerência dos dados, suporte ao processo, integração de ferramentas e
comunicação humana.
A tecnologia de ambientes de desenvolvimento de software (ADS) evolui à
medida
que
são
identificadas
as
reais
necessidades
do
processo
de
desenvolvimento de software e sua integração com outras tecnologias. Portanto, o
conhecimento dessa evolução leva a uma melhor análise das principais
características e requisitos destes ambientes. Dentre as principais características,
23
destacam-se a integração entre ferramentas e a arquitetura do ambiente, a serem
tratadas neste capítulo.
Logo em seguida, será apresentada, também, a evolução das ferramentas
de SCM e os principais conceitos envolvidos.
2.1.1 Ambientes de Desenvolvimento de Software (ADS)
As primeiras definições relacionadas aos ADS foram publicadas na década
de 80 através do relatório Stoneman [BRO 92a]. Este relatório definiu a arquitetura
de um ambiente de suporte à programação em Ada chamado APSE4 e foi o
precursor dos trabalhos subseqüentes em ADS.
A evolução da tecnologia de ADS iniciou a partir da construção de
ferramentas individuais de apoio a programação, como compiladores, montadores
e depuradores. Em seguida houve o surgimento de ferramentas de apoio a outras
fases do desenvolvimento de software, como análise e projeto. Tais ferramentas
têm como característica principal o suporte a determinados métodos de
desenvolvimento e ficaram conhecidas como ferramentas CASE.
Tipicamente,
as
ferramentas
CASE
suportam
algum
método
de
desenvolvimento para usuários individuais provendo facilidades na interface com o
usuário para a construção de sistemas de informação.
As ferramentas CASE evoluíram para ambientes CASE integrados (I-CASE
- Integrated CASE), os quais são compostos de várias ferramentas capazes de
transferir informações para outras ferramentas [PRE 95]. Entretanto, a
característica de suporte a um método específico permaneceu nos I-CASEs. Um
24
ADS tem como objetivo prover um ambiente no qual produtos de software de
grande porte possam ser desenvolvidos através da integração de um conjunto de
ferramentas que suportam métodos de desenvolvimento, apoiados por uma
estrutura que permite a comunicação e cooperação entre as ferramentas [BRO
92a].
Este conceito veio como resposta ao reconhecimento de que a
comunicação e a coordenação entre todas as ferramentas usadas no
desenvolvimento e manutenção de software são essenciais para a produção
eficiente de software de qualidade. Uma das metas dos estudos nesta área tem
sido obter um bom entendimento sobre como as ferramentas são integradas,
definidas, implementadas, adaptadas e desenvolvidas.
As diferenças existentes entre ADS e ferramentas CASE são abordadas em
[BRO 92b]. Entretanto, o conceito de ADS é considerado bem mais amplo por
derivar do trabalho de Stoneman e prover uma estrutura unificadora de serviços
onde várias ferramentas de diferentes métodos podem ser integradas.
Para que o ADS proporcione uma garantia da qualidade do produto e do
processo, o processo de software utilizado na organização deve estar formalizado
e ser obedecido. O ambiente, então, usa o modelo de processo de software
descrito em uma linguagem de modelagem do processo para executar de forma
independente aquelas tarefas que podem ser completamente automatizadas,
coordenar tarefas mais complexas, e garantir informação de gerência atualizada,
eliminando cargas administrativas desnecessárias dos usuários. ADS com essas
características são conhecidos como ADS orientados ao processo (ou centrados
em processo).
25
Estes ambientes conhecem o modelo de processo, permitem modificações
no mesmo e possuem mecanismos para executá-lo. Além disso, o ADS necessita
manter um repositório comum com todos os objetos produzidos pelas ferramentas,
todos os dados sobre o próprio processo e de todos os produtos já desenvolvidos
e em desenvolvimento.
2.1.1.1 Integração de Ferramentas
Ambientes de desenvolvimento de software são compostos de ferramentas.
Estas ferramentas deverão compartilhar informações sobre os vários projetos e os
usuários necessitarão utilizar em uma ferramenta uma informação gerada por
outra ferramenta. Portanto, o ADS deve prover uma estrutura unificadora que
integre suas ferramentas. Devido a essa característica, os ambientes de
desenvolvimento de software são comercialmente conhecidos como “ambientes
integrados de desenvolvimento” (IDE – Integrated Development Environment).
O conceito de integração pode ser interpretado de diferentes formas e
possui várias classificações. Segundo [THO 92], a integração é uma propriedade
dos relacionamentos entre as ferramentas que formam um ambiente e refere-se
ao grau de “acordo” que existe entre essas ferramentas.
A integração é um elemento chave para o suporte ao desenvolvimento de
software. Os principais aspectos de integração em ADS são [BRO 92a]:
Integração de Interface: Cada ferramenta possui o mesmo conjunto de
construtores na interface com o usuário, ou seja, os usuários interagem com todas
as ferramentas do ambiente da mesma forma;
26
Integração de Processo: O ambiente suporta um processo de software definido;
Integração de ferramentas: Ferramentas compartilham dados através de um
mesmo formato de dados e podem ser facilmente combinadas;
Integração de equipes: O ambiente promove o trabalho em grupo assegurando
comunicação efetiva e disseminação de informação;
Integração de gerência: O ambiente auxilia os gerentes a controlarem o
desenvolvimento. A informação de gerência é derivada das informações técnicas
produzidas por engenheiros de software.
Com relação à integração de ferramentas, os ambientes podem se
enquadrar em vários níveis, segundo [BRO 92a]. Estes níveis (carregador, léxico,
sintático, semântico e de método) não são estritamente hierárquicos, apenas
progridem de baixo grau de integração a alto grau de integração.
Os fatores cruciais na avaliação do grau de integração entre ferramentas
em um ADS são as formas de armazenamento, compartilhamento e transferência
de informações entre as ferramentas. Os níveis são mostrados na Figura 2 e
apresentados a seguir.
27
Figura 2 - Níveis de integração de ferramentas em um ADS [BRO 92].
Nível Carregador: Neste nível, cada ferramenta analisa seus dados de entrada e
saída pois não há entendimento comum sobre os dados que as ferramentas
podem tratar. Um exemplo é o ambiente UNIX onde a integração provida é através
da composição de ferramentas em um formato de arquivos simples e consistente.
Cada ferramenta é responsável pelos seus dados e o compartilhamento de
informações se dá pelo fato de todas as entradas e saídas de ferramentas serem
na forma de um fluxo de bytes, não existindo nenhuma forma de cooperação mais
aprofundada entre as ferramentas;
Nível Léxico: Neste nível, grupos de ferramentas compartilham formatos de
dados e convenções de operações, o que permite sua interação. Porém, para
adicionar uma nova ferramenta ao grupo, a comunicação e os formatos de dados
dessa ferramenta devem ser compreendidos, e isto se torna bastante trabalhoso;
28
Nível Sintático: Neste nível um conjunto de regras de formação de estruturas de
dados é compreendido por todas as ferramentas do ambiente, não sendo
necessário que cada ferramenta analise, valide e converta estrutura de dados de
outras ferramentas. Este é o nível encontrado na maioria dos ADS, tipicamente na
forma de um esquema de banco de dados que pode ser consultado por todas as
ferramentas do ambiente;
Nível Semântico: Para aumentar a integração, um entendimento das estruturas
de dados deve ser acompanhado de uma definição da semântica dessas
estruturas. Estão disponíveis as definições das estruturas de dados mais os
significados das operações sobre essas estruturas;
Nível de Métodos: Neste nível as ferramentas, além de estarem no nível
semântico, são usadas no contexto do processo de software implicando que as
ferramentas “entendam o processo”. Neste nível as ferramentas devem concordar
com as estruturas de dados, operações e com o processo de desenvolvimento
específico. Este nível somente pode ser obtido por um ADS orientado a
processos.
2.1.1.2 Arquitetura
O modelo de referência ECMA (European Computer Manufacturers
Association) [BRO 92a], descreve uma arquitetura para ambientes integrados de
desenvolvimento de software. Um modelo de referência é uma estrutura conceitual
29
e funcional que facilita a descrição e comparação de sistemas, não devendo ser
utilizado como uma especificação de implementação [BRO 92a].
O modelo ECMA segue uma visão orientada a serviços abstraindo detalhes
de implementação e concentrando-se nas capacidades do ambiente. Em ADS
orientados a processo, a escolha do processo específico utilizado em um projeto
de desenvolvimento é livre e sua definição e execução é suportada por um
conjunto de serviços de gerência do processo: desenvolvimento, execução,
visibilidade, monitoração, transação e serviços de recursos. A Figura 3 apresenta
o modelo de referência ECMA. O diagrama não deve ser interpretado como um
conjunto de camadas. Os serviços de mensagem permitem comunicação nos dois
sentidos: serviço a serviço, ferramenta a ferramenta e serviço a ferramenta.
Figura 3 - Modelo de Referência ECMA [BRO 92a]
30
As ferramentas são componentes de software que não fazem parte da
arquitetura do ambiente, mas que utilizam os serviços dessa estrutura e estão
integradas em nível de método.
A seguir são descritos os serviços do modelo ECMA:
•
Os serviços de repositório de dados mantêm, gerenciam e nomeiam
entidades de dados ou objetos e seus relacionamentos. Algum
suporte a execução do processo também é tratado por esses
serviços além do suporte à distribuição física de dados e processos;
•
Serviços de integração de dados incrementam os serviços de
repositório provendo uma semântica de alto nível e operações para
tratar os dados armazenados no repositório. Estes serviços incluem
tratamento de versões e configurações;
•
Os serviços de gerência do processo (ou serviços de gerência de
tarefas) provêem uma camada de abstração que permite ao usuário
lidar com as tarefas, aumentando a "inteligência" do ambiente.
•
Os serviços de mensagem fornecem um padrão de comunicação que
pode ser usado pelas ferramentas e outros serviços.
•
Os serviços de interface com o usuário provêem consistência de
interface adotada em todas as ferramentas.
31
Além desses serviços, a estrutura ainda provê mecanismos de segurança.
Mais detalhes sobre os serviços providos pela arquitetura podem ser encontrados
em [BRO 92a].
Com esta padronização novas ferramentas poderiam ser integradas ao
ambiente permitindo que os usuários percebessem todos os níveis de integração
do ambiente [CHE 92].
2.1.2 Software Configuration Management (SCM)
Os conceitos de SCM são normalmente associados com as suas
funcionalidades e atividades. Sistemas diferentes de SCM implementam vários
dos
conceitos
descritos
no
“IEEE
Standard
for
Software
Configuration
Management Plans”. Segundo [WU 2003], as atividades de SCM são
tradicionalmente agrupadas em quatro categorias: identificação de configuração,
controle de configuração, contabilidade de estado e auditoria e revisões. Essas
quatro funções são descritas no IEEE Standard 828-1990 como segue:
Identificação: identificar, nomear e descrever as características físicas e
funcionais do código fonte, especificações, projeto, e elementos de dados a serem
controlados pelo projeto;
Controle: requisitar, avaliar, aprovar ou desaprovar e implementar mudanças;
32
Contabilidade de Estado: gravar e solicitar o estado dos itens de configuração do
projeto (por exemplo, versão aprovada inicial, estado das mudanças solicitadas,
implementação das mudanças aprovadas);
Auditorias e Revisões: determinar que extensão do item atual de configuração
reflete as características físicas e funcionais solicitadas.
Com o crescimento e desenvolvimento de várias ferramentas de SCM, o
Software Engineering Institute (SEI) da universidade de Carnegie Mellon reviu as
ferramentas de SCM e evoluiu a definição de SCM adicionando três outras
funções [DAR 91], são elas:
Manufatura: gerenciar a construção e construir o produto de uma maneira ótima;
Gerência de Processo: assegurar o uso dos procedimentos, políticas e modelo
de ciclo de vida da organização;
Trabalho em Equipe: controlar o trabalho e as interações de múltiplos usuários
em um produto.
Essas três funções complementam a definição do padrão IEEE para tornar
a definição de SCM mais detalhada e completa. Elas também estão de
conformidade com o conceito de SCM, que diz que SCM não é somente um
processo, mas também uma disciplina de gerenciamento [WU 2003]. Entretanto,
33
não existem regras fixas para implementar as funcionalidades de SCM, a maioria
dos sistemas implementa somente uma parte de todas as funcionalidades e outros
uma nova combinação de funcionalidades, que é única para suas tarefas
específicas. Como o objetivo do SCM é fazer software de qualidade e não existe
um padrão universal que constitui um sistema de SCM, todos os sistemas que
fornecem funcionalidades parciais, mas, pretendem fornecer um SCM com todas
as funcionalidades para conseguir alta qualidade de software, são considerados
sistemas de SCM pela comunidade de engenharia de software [DAR 91].
A despeito da variedade de implementações de SCM, diversos conceitos
básicos têm sido adotados no projeto e implementação de todos os sistemas de
SCM. Esses conceitos básicos incluem itens de configuração, versão, release e
baseline.
Item de Configuração de Software (SCI – Software Configuration Item): é uma
agregação de software que é designada para gerenciamento de configuração e é
tratada como uma simples entidade no processo de SCM. Normalmente, SCIs
incluem especificações, segmentos de código fonte, bibliotecas de código,
documentações, e manuais de usuário e desenvolvedor;
Versão: um termo genérico usado para descrever um release inicial de um SCI.
Diferentes sistemas de controle de versão têm diferentes interpretações para este
termo. Por exemplo, no CVS, “versão” corresponde a um arquivo. Versões de
projetos são produzidas através de um tagging em um grupo de arquivos num
momento particular;
34
Release: a notificação e distribuição de uma versão aprovada;
Baseline: é um conjunto de itens de software formalmente designados e
reparados em um tempo especifico durante o ciclo de vida do software. Pode-se
dizer que um baseline é uma versão específica usada como ponto de referência
para desenvolvimento futuro. Porém, uma baseline é diferente de uma versão.
Tipicamente, um baseline representa um marco conceitual no processo de
desenvolvimento de software.
2.1.2.1 Sistema de Controle de Versão (VCS - Version Control System)
Controle de versão é uma função importante de SCM. Como descrito
acima, um sistema que implementa parcialmente o padrão SCM é considerado um
sistema SCM, conseqüentemente, um sistema de controle de versão é
considerado um sistema de SCM. Controle de versão envolve o armazenamento
de itens de configuração, o armazenamento de mudanças, a gerência dessas
mudanças, e a função mais importante, a facilitação e gerenciamento do
desenvolvimento colaborativo de software, que permite que vários integrantes da
equipe trabalhem no mesmo conjunto de código, enviem suas mudanças, e se
comuniquem com os outros integrantes [WU 2003].
35
2.1.2.1.1 Termos
Existem alguns termos comuns que são compartilhados pelos vários
sistemas de controle de versão independentemente dos conceitos básicos de
SCM descritos na seção 2.1.2.
Revisão: Uma mudança cometida no histórico de um item de configuração. Existe
uma linha tênue entre a definição de revisão e versão, e as pessoas tendem a
confundi-las. De fato, diferentes ferramentas de controle de versão usam a
terminologia em caminhos ligeiramente diferentes, como mencionado na seção 2.
No CVS, uma revisão de um arquivo pode pertencer a mais que uma versão de
um projeto, desde que a revisão de um arquivo possa ser “tagged” várias vezes.
Repositório: uma biblioteca centralizada de arquivos que estão sob o controle de
versão. A noção de repositório foi inicialmente proposta com o Revision Control
System (RCS) que foi desenvolvido por Walter F. Tichy. Todas as informações de
CM sobre arquivos e o conteúdo desses arquivos são mantidas no repositório;
Workspace: o espaço no qual os programadores executam as tarefas de
desenvolvimento;
Working Copy: a cópia dos arquivos do repositório no espaço de um usuário.
36
2.1.2.1.2 Atividades Comuns
A finalidade de uma ferramenta de controle de versão é suportar as
funcionalidades de SCM. Embora sistemas de controle de versão diferentes
implementem modelos de controle de versão diferentes, existem algumas
características comuns entre eles. Segue um resumo das atividades comuns
associadas com os Sistemas de Controle de Versão.
Conexão de Dados: como mencionado acima, a noção de um repositório é
extensamente usada em sistemas de controle de versão. Um repositório pode ser
um sistema de arquivos local ou um servidor remoto, e providencia árvore de
versão para gerenciar versões diferentes de arquivos. Por exemplo, várias
ferramentas de controle de versão usam arquitetura de cliente/servidor e os
clientes podem pegar quaisquer informações sobre qualquer arquivo acessando o
repositório;
Check-in/Check-out: os usuários submetem suas mudanças através de “checkin”, os artefatos de software de seus workspaces pessoais são enviados para o
repositório compartilhado, e recuperam artefatos de software do repositório
através do “check-out”;
Versioning: depois de tudo submetido, a ferramenta de controle de versão atribui
um novo número de revisão para os arquivos modificados, e grava as informações
relacionadas, como tempo, usuário, etc;
37
Operação de Diff: quase todas as ferramentas de controle de versão
providenciam mecanismos para os usuários compararem as diferenças entre duas
revisões de arquivos selecionados. Uma operação de “diff” ideal permitiria a
comparação entre uma cópia de trabalho de um usuário e qualquer revisão no
repositório, como também, uma comparação entre duas revisões quaisquer.
Algumas ferramentas providenciam somente parte desta funcionalidade, enquanto
outras providenciam mecanismos mais sofisticados;
Operação de Get: este mecanismo é fornecido para os usuários recuperarem
qual revisão antiga dos arquivos ou versões de um projeto. Ela é usada quando os
usuários procuram versões antigas melhores que a corrente e querem reverter
para uma dessas versões;
Operação de Report: esta operação é usada para gerar vários relatórios úteis
sobre os artefatos de software, como o histórico de um artefato, anotações, etc.
2.1.2.1.3 Algumas Ferramentas de Controle de Versão Existentes
Foram investigados diversos sistemas de controle de versão populares para
selecionar o sistema de controle de versão a ser utilizado. Descobrimos que
alguns deles são bem projetados e implementados e tem um número considerável
de usuários.
38
Rational ClearCase
Usa um único modelo para a finalidade de configuração e gerenciamento.
Os conceitos básicos do UCM são “atividade” e “artefato”. Uma “atividade” é uma
parte de um trabalho a ser realizada no projeto. Um “artefato” é um item,
normalmente, um arquivo que está sob o controle de versão. Seu modelo de
controle de versão atribui versão a todos os artefatos no ciclo de vida do
desenvolvimento de software. O artefato pode ser um simples arquivo, um
diretório, um subdiretório e todos os tipos de objetos do sistema de arquivos. Esta
é sua diferença, a maioria dos outros VCS somente suporta controle de versão de
arquivos [RAT 2006].
Perforce
Possui uma interface com o usuário amigável, no Perforce Windows cliente
chamado P4Win, uma interface padrão Microsoft é usada para mostrar as
informações relacionadas a todas as tarefas de SCM. Os trabalhos em andamento
são mostrados e um mecanismo de arraste-e-solte é disponibilizado para o
usuário realizar suas tarefas. Ele não apenas disponibiliza uma visão do diretório
de trabalho, como outro CVS, mas também uma visão do repositório como um
todo. Outra vantagem do P4Win é que ele providencia uma visão da lista de
tarefas do usuário, que é excelente para pessoas que trabalham em equipe para
verificar os estados do andamento de trabalho de seus colegas [PER 2006].
39
Revision Control System (RCS)
Permite controle de versão simples e básico dos arquivos que estão no
repositório. Os arquivos são armazenados no repositório na forma de árvore. Cada
arquivo tem todas as suas revisões em uma árvore ancestral. A raiz da árvore é a
revisão mais antiga deste arquivo, e as revisões sucessivas são ligadas aos seus
ancestrais. RCS têm seu próprio esquema para atribuir números de revisões para
os arquivos. No RCS, somente os arquivos correntes são armazenados no
repositório, que conhece somente as diferenças entre as revisões armazenadas, o
que economiza muito espaço em disco, mas aumenta o tempo de acesso [TIC 85].
Concurrent Versions System (CVS)
É um sistema de controle de versão de código fonte aberto. O CVS usa o
formato para armazenamento de arquivo do RCS, entretanto, ele é mais
sofisticado e suporta muitas outras funções, a maior diferença é que o CVS
suporta um ambiente de desenvolvimento concorrente, enquanto que o RCS
somente suporta um simples usuário. Esta função é importante hoje em dia
porque a programação em equipe é o estilo de programação que esta
prevalecendo na comunidade de engenharia de software. Porém, as vantagens do
CVS também introduzem desvantagens. Devido a suas poderosas funções, ele
requer maior administração e o grande número de comandos produz uma curva
de aprendizagem íngreme para os novatos. Existem alguns termos definidos
especialmente no CVS. Uma tag é o nome atribuído a uma coleção de revisões de
arquivos em um momento particular de um histórico de versão. Uma versão de um
projeto no CVS normalmente é determinada por uma tag. Outro conceito é o
40
conceito de branch. Um CVS branch corta o desenvolvimento de um projeto em
histórias paralelas e as mudanças feitas em um branch não afetam o outro [CVS
2006].
Subversion (SVN)
O Subversion foi desenvolvido para ser um CVS melhorado, por isso ele
possui a maioria das características do CVS. Geralmente, a interface do
Subversion para uma característica em particular é parecida com a do CVS,
exceto onde há uma razão específica para isso não acontecer. O controle de
versão ocorre sobre diretórios, arquivos e meta-dados. A falta dessas
características é uma das queixas mais comuns do CVS. O Subversion controla
versões não somente do conteúdo e da existência dos arquivos, mas também de
diretórios, cópias e renomeações. Ele também permite o controle de executáveis e
os commits são verdadeiramente atômicos. O controle do número de revisões é
feito por commits, e não por arquivos. As mensagens de log são anexadas na
revisão e não gravadas de forma redundante como no CVS [SVN 2006].
O Subversion tem a opção de trabalhar com o Apache, através do protocolo
WebDAV/DesltaV, podendo usar este protocolo para as comunicações de rede e o
servidor Web Apache para permitir serviços de repositório no lado do servidor. Isto
dá ao Subversion uma vantagem sobre o CVS em interoperabilidade, e
providencia várias características importantes, como: autenticação, autorização
baseada em arquivo, compressão, e visualização Web do repositório. O
Subversion permite a opção de trabalhar como um servidor standalone, e pode ser
acessado via SSH. Branching e tagging são operações implementadas sobre a
41
operação de cópia, que consome pouco espaço e recursos de hardware. Uma
cópia é uma tag e, se for iniciado um commit em uma cópia, isso se caracteriza
como um branch.
O SVN foi desenvolvido para ser um sistema cliente/servidor, evitando
assim alguns dos problemas de manutenção que tem atingido o CVS. O código é
estruturado em um conjunto de módulos com interfaces bem definidas,
desenvolvidas para serem chamadas por outras aplicações [WOR 2005].
O protocolo cliente/servidor envia diffs em ambas as direções, o protocolo
de rede usa a banda eficientemente para transmitir diffs em ambas as direções
sempre que possível. Já o CVS envia diffs do servidor para o cliente, mas nunca
do cliente para o servidor. Os custos são proporcionais aos tamanhos das
mudanças, não ao tamanho dos dados. Em geral, o tempo necessário para uma
operação no SVN é proporcional ao tamanho das mudanças resultantes da
operação e não ao tamanho absoluto do projeto em que as mudanças estão
acontecendo, o que é uma propriedade do modelo de repositório do Subversion.
O Subversion permite implementações do repositório baseadas em texto
plano ou em banco de dados e controla a versão de liks simbólicos.
A seguir faremos uma breve comparação entre o CVS e o Subversion
baseando-se nos seguintes aspectos: operações no repositório, características,
estado técnico, interface com o usuário e licenciamento.
2.1.2.1.4 Comparação entre o CVS e o SVN
Como citado no item anterior, existem várias ferramentas para controle de
versão, algumas open-source e outras comerciais. A integração de uma
42
ferramenta comercial poderia não ser possível se ela não disponibilizasse uma
API para integração, ou poderia ser impossibilitada pelos mecanismos legais,
como licenciamento, etc. Assim, deveria ser escolhida uma ferramenta opensource para ser integrada. As duas principais ferramentas open-source existentes
são o CVS e o Subversion, fez-se, então, uma comparação entre essas duas
ferramentas com o intuito de justificar a escolha do Subversion.
A seguir algumas tabelas comparando várias características das duas
ferramentas [WOR 2005].
Operação
Commit Atômico
Mover e renomear
arquivos
CVS
Não
SVN
Sim
Descrição
Se uma operação no repositório é interrompida, o
repositório não fica em um estado inconsistente.
Suporta mover um arquivo ou diretório para uma
Não
Sim
localização diferente e mantêm o histórico dos
arquivos.
Copias de
arquivos e
Não
Sim
Não
Sim
mudanças para os Não
Sim
Suporta cópia de arquivos ou diretórios para uma
localização diferente e mantêm seu histórico.
Diretórios
Replicação
Remota do
Suporta copiar um repositório remoto para ter uma
cópia equivalente no sistema local.
Repositório
Propagação de
Suporta a propagação de mudanças de um
repositório para outro.
Repositórios pais
43
Permissões do
Suporta permissões de acesso para diferentes
Limitado Sim
Repositório
Suporte a
Não
Changesets
partes do repositório remoto
Parcial
Tabela 1 - Comparação entre as operações no repositório.
Característica
Habilidade
CVS SVN
Descrição
para
trabalhar somente
com um diretório
Sim
Sim
Não
Sim
Suporta checkout de somente um diretório do repositório
ou é restrito a todo o repositório.
do repositório
Mensagens
de
commit por arquivo
Suporta mensagens de commit por arquivo.
Tabela 2 - Comparação entre as características operacionais dos sistemas.
Característica
CVS
SVN
Documentação
Excelente
Boa
Sim
Sim
Descrição
Como é a documentação disponível, tutoriais,
livros, etc.
Fácil Descoberta
de erro
Possui ferramentas de deployment.
(deployment)
Como é a integração de rede nesse sistema. É
Suporte a rede
Boa
Excelente compatível com os protocolos e infraestruturas existentes.
44
Portabildiade
Boa
Excelente
Quão portável é o sistema para os vários
sistemas operacionais e arquiteturas.
Tabela 3 - Comparação entre as características gerais.
Interface
CVS
SVN
Descrição
Possui uma interface baseada em WWW que pode
Web
Sim
Sim
ser usada para explorar a árvore e as várias revisões
dos arquivos.
GUI
Sim
Sim
Possui uma interface gráfica com o usuário
Tabela 4 - Comparação entre as interfaces dos sistemas.
Licenciamento
CVS
Tipo de
licenciamento
GNU GPL (open source)
SVN
Apache/BSD-style license. (opensource).
Tabela 5 - Comparação entre as formas de licenciamento dos sistemas.
45
3 O Ambiente NetBeans
A NetBeans.org é uma das maiores comunidades de desenvolvimento de
software de código fonte aberto (Open Source Software Development - OSSD),
com 1100 usuários ativos, 500 contribuintes, 9500 alterações de código fonte por
mês, 1.7 milhões de downloads, e algo em torno de 100.000 participantes de listas
de email. Ela é uma comunidade focada em JAVA, patrocinada pela Sun
Microsystems e voltada a criar um ambiente integrado de desenvolvimento de
software (IDE) para desenvolvimento de aplicações JAVA e, também, uma
plataforma para desenvolvimento de outros produtos [JEN 2005].
A comunidade iniciou como um projeto de estudantes, em 1996, que foi,
adquirido e, logo em seguida, liberado como uma comunidade de código fonte
aberto pela Sun, de quem a equipe NetBeans recebe muitos dos colaboradores
[NET 2006].
O IDE NetBeans é desenvolvido em JAVA, assim, ele funciona em qualquer
plataforma que suporte JDK. Além disso, a configuração de tempo de execução do
NetBeans é descrita em XML. Quando o core do NetBeans é carregado, ele
configura e lança o resto do sistema. O core lê a descrição XML e carrega os plugins apropriados, que são componentes escritos em Java que implementam a sua
API.
É este conceito de plug-in que permite a extensão do NetBeans. Muitas das
mais importantes funcionalidades do NetBeans são plug-ins que podem ser
desconectados do sistema e atualizados quando necessário, e, como o IDE
46
NetBeans é um software de código fonte aberto, pode-se modificar e recompilar
qualquer plug-in da maneira que se desejar.
3.1 A Arquitetura do IDE NetBeans
A arquitetura do IDE NetBeans divide-se em duas seções principais: O core
(também chamado de application runtime) e as APIs abertas. Elas podem ser
encontradas
nos
pacotes
JAVA
org.netbeans.core.*
e
org.openide.*,
respectivamente. As bibliotecas binárias desses arquivos fazem parte da
instalação do NetBeans e estão nos arquivos $NB_HOME/lib/core.jar e
$NB_HOME/lib/openide.jar. O core implementa muitas interfaces definidas nas
APIs abertas e o runtime engine do NetBeans. As APIs abertas são o conjunto de
ferramentas disponíveis para se escrever plug-ins que funcionarão dentro do
NetBeans. Um plug-in é um arquivo .jar (Java ARchive ou JAR) que contém
classes JAVA e um arquivo denominado manifest que descreve para o NetBeans
runtime o que é o plug-in, como instalá-lo e desinstalá-lo [GRE 2002].
Tanto para IDEs como para qualquer outra aplicação, essa arquitetura traz
inúmeras vantagens. Como, por exemplo, a habilidade para suportar múltiplas
línguas ou a habilidade para envolver a funcionalidade de outras ferramentas de
desenvolvimento e apresentá-la dentro do IDE. Um simples exemplo deste tipo de
habilidade são os compiladores externos e máquinas virtuais JAVA. Eles podem
ser usados para compilação e execução definindo-se serviços que lançam um
processo externo e mostram a saída deste processo (ex. mensagens de erro do
compilador). Um exemplo mais complexo seria a integração de uma ferramenta de
47
XML pré-existente, ela poderia ser integrada diretamente através da interface
gráfica do NetBeans.
Alguns dos elementos do NetBeans, como o Sistema de Arquivos, Nós e
API Explorer, podem também ser usados como bibliotecas para aplicações
externas ao NetBeans. Assim, em certo sentido, o NetBeans é um conjunto de
bibliotecas.
Uma desvantagem deste tipo de modelo é que os plug-ins não podem
chamar funcionalidades que não aparecem nas APIs abertas, por exemplo, um
objeto que tem uma interface declarada na API aberta e que modelará uma classe
que implementa essa interface mas possui alguns métodos que não são definidos
na API. O uso de uma API garante acesso a características que continuarão
funcionando mesmo se a implementação for alterada, porém, isto implica que não
há nenhuma garantia que qualquer característica não declarada na documentação
das APIs abertas estarão presentes nas versões futuras do IDE, assim qualquer
código que use as classes que implementam o core diretamente e não as
interfaces presentes nas APIs podem não funcionar em versões futuras do
NetBeans.
3.2 O Desenvolvimento de Plug-ins no IDE NetBeans
Os plug-ins interagem com o NetBeans através das APIs Abertas, sendo
assim, para o desenvolvimento dos plug-ins faz-se necessário entender como as
APIs abertas são implementadas e usadas. Nesta seção serão introduzidos os
principais conceitos utilizados pelos plug-ins que estenderão o IDE.
48
Internamente o NetBeans chama os plug-ins de módulos, assim, quando
nos referirmos a módulos, estamos nos referindo a plug-ins NetBeans.
3.2.1 As APIs Abertas (Open APIs)
As APIs abertas são o conjunto de interfaces, definidas no pacote
org.openide e em seus subpacotes, e suas especificações (como arquivos
manifest e camadas XML) definidas na documentação. Elas podem ser definidas,
também, como as interfaces dos módulos com o NetBeans. As APIs abertas
podem ser divididas em seções, como segue:
Sistema de Arquivos: Provê comunicação com a camada de persistência dos
dados;
Sistemas de Dados: Provê reconhecimento e interpretação de tipos diferentes de
dados;
Ações: Provê a abstração da invocação dos usuários;
Nós: Provê relacionamento hierárquico entre dados e objetos e alguns aspectos
de como eles são apresentados para o usuário;
Explorer: Provê apresentação das estruturas hierárquicas dos dados.
49
Serviços/Lookup: Provê a localização de objetos ou serviços providenciados
pelos módulos, que podem ser usados por outros módulos ou invocados pelo
usuário. Os serviços são usados normalmente para executar operações
complexas em grupos de objetos, como compilação, execução e procura.
Sistema de Janela: Provê manipulação e configuração de janelas e componentes
visuais da interface com o usuário.
3.2.2 Modularidade
A Modularidade é uma das características principais do NetBeans. Ele
implementa a idéia de que funcionalidades podem ser discretamente trabalhadas
e a possibilidade de adicioná-las e removê-las tranquilamente [GRE 2002]. A API
dos módulos define o que um módulo é e como instalá-lo e desinstalá-lo. Cada
módulo pode oferecer, também, sua própria API para que outros módulos utilizem
as suas funcionalidades, por exemplo, os módulos XML oferecem suporte básico a
documentos XML e podem ser usados por outros módulos que desejem trabalhar
com XML. Para casos como esses, existe um caminho para declarar as
dependências entre os módulos. O NetBeans runtime não permitirá que um
módulo seja instalado se este módulo requer um outro que não está presente,
como também não permitirá que um módulo seja desabilitado, se outros
dependem dele.
50
3.2.3 Hierarquia, Arquivos e Nós
Aplicações grandes e extensíveis necessitam dispor de caminhos para que
os componentes do sistema criem objetos e notifiquem outras partes do sistema
que utilizam esses objetos. Elas necessitam, também, de caminhos para
apresentar esses objetos para o usuário.
É necessário um sistema genérico que possa fornecer funcionalidades
específicas para cada tipo de dado. Para resolver este problema, existe um
paradigma muito conhecido utilizado nos Sistemas Operacionais: Arquivos.
Aplicações rodando em um sistema operacional criam arquivos no disco e
trabalham com ele. O sistema operacional providencia serviços de baixo-nível
para criar e acessar arquivos, e não está interessado no conteúdo dos arquivos
criados pelos usuários. Os módulos no NetBens são razoavelmente análogos a
aplicações em um sistema operacional, e o core gerencia os objetos persistidos
criados pelos usuários [GRE 2002]. O NetBeans tem um conceito de um sistema
de arquivos, que em seu sentido mais básico, significa uma área de
armazenamento em que os arquivos podem ser escritos e lidos. Em outro sentido,
o sistema de arquivos pode ser entendido como uma fábrica de objetos JAVA.
Para o segundo problema, a apresentação, o NetBeans provê uma
abstração sem conhecimento de conteúdo, o “nó”, que é usado para conter a
representação hierárquica dos dados e a apresentação desses dados através da
interface com o usuário. Um nó não é um recipiente de dados, nem um ponteiro
para os dados, ele é, assim como os sistemas de arquivos, um objeto de dados,
51
que identifica tipos de dados e agrega múltiplos arquivos relacionados em uma
única entidade.
3.2.4 A Abstração de Arquivos no NetBeans
O sistema de arquivos no NetBeans é um lugar onde pode-se,
hierarquicamente, ler e escrever arquivos. Mas o NetBeans não requer que um
sistema de arquivos trabalhe necessariamente com arquivos no disco, somente
requer que ele se comporte como tal. Isto pode ser realizado graças a
implementação de um conjunto de interfaces definidas na API de sistemas de
arquivos.
Um sistema de arquivos precisa somente estar de conformidade com esta
interface. Como e onde os dados são fisicamente persistidos é irrelevante e
transparente para o código que trabalha com os arquivos. Um sistema de arquivos
é, efetivamente, um espaço de nomes hierárquico para entidades nomeadas que
contêm dados ou, contém entidades que contêm dados, ou seja, uma interface de
fornecimento de serviço (Service Provider Interface - SPI) para estender o
NetBeans com tipos alternativos para armazenamento de arquivos. Como um
exemplo concreto, o NetBeans define, e internamente usa, um sistema de
arquivos XML que é, para todas as intenções e finalidades, análogo a arquivos
físicos em disco [GRE 2002]. Assim, existem extensões para suportar FTP, CVS e
outros tipos de armazenamento disponíveis em módulos separados.
Se para existir um sistema de arquivos, a única necessidade é satisfazer as
características mostradas acima, é possível existir um sistema de arquivos virtual
52
que possui uma coleção de subsistemas de arquivos e apresenta todos eles no
mesmo espaço virtual. O NetBeans contém uma implementação com essas
características na classe MultiFileSystem, que faz parte da API de sistemas de
arquivos. Esta classe permite que seja construído um simples espaço de nomes
que contém um conjunto de subsistemas de arquivos, chamados “camadas”, e
funciona como se o conteúdo de todos eles estivessem no mesmo espaço de
nomes, como pode ser visto na Figura 4.
Figura 4 – MultiFileSystem [GRE 2002].
Se duas camadas diferentes contém uma pasta chamada /MinhaPasta/ com
conteúdos diferentes, ao listar-se o conteúdo de MultiFileSystem’s / MinhaPasta /
simplesmente o conteúdo das duas pastas serão listados. Para negociar com
colisão de nomes (por exemplo, duas camadas contém arquivos diferentes com o
mesmo nome e caminho), a classe MultiFileSystem possui uma ordem de
empilhamento das camadas. No caso de um conflito, qualquer sistema de arquivo
no topo da pilha terá preferência. É possível, também, incluir ou remover camadas
em tempo de execução.
53
3.2.5 Mapeando Arquivos Para Objetos
Apenas dispor de maneiras de salvar arquivos não é suficiente em um
ambiente integrado, é necessário dispor de maneiras de salvar instâncias de
objetos. Assim, o NetBeans define semânticas para mapear arquivos para objetos.
Pode-se criar, por exemplo, um arquivo de nome com-mycom-MyClass.instance. A
API de sistema de dados contém uma facilidade (implementada em uma classe
chamada InstanceDataObject) que referencia um arquivo pelo nome e retorna
uma instância da classe nomeada, assim, uma instância da classe MyClass seria
retornada.
Usando-se essa infra-estrutura, arquivos podem ser fábricas para objetos.
Esta infra-estrutura serve para duas finalidades principais no NetBeans, primeiro:
habilita módulos a registrarem objetos no sistema (por exemplo, adicionar BEANS
para a paleta de componentes ou adicionar itens em um menu); segundo: permite
que módulos sejam registrados sem que a JVM carregue a classe em questão até
que seja realmente necessário, não usando memória desnecessariamente.
No topo desta infra-estrutura está a facilidade chamada lookup. Lookup
adiciona uma camada para ação indireta permitindo que o código do módulo
procure objetos registrados através de arquivos em pastas especiais sem
trabalhar diretamente com sistemas de arquivos [GRE 2002].
Nós mencionamos na seção 3.1.2 o conceito de um sistema de arquivos
XML, onde o conteúdo do sistema de arquivos está atualmente representado em
um documento XML. No sistema de arquivos XML do NetBeans, o XML para
54
adicionar uma classe JAVA para um sistema de arquivos seria algo como o código
mostrado na Figura 5.
Figura 5 - Adicionando um .instance para o Sistema de Arquivos [GRE 2002].
O resultado do exemplo detalhado na figura acima é que quando o sistema
cria
o
menu
principal,
ele
cria
uma
instância
de
org.netbeans.examples.quickpanel.ShowQuickPanelAction, que fornece o ícone e
mostra o nome da ação do item de menu que será adicionado. Quando o usuário
selecionar o ícone do item de menu adicionado, o método actionPerformed() da
instância criada será chamado.
3.2.6 O Sistema de Arquivos do NetBeans
O Netbeans não somente usa o conceito de sistema de arquivos virtual
para manipular arquivos do usuário, como também usa um sistema de arquivos
virtual especial que contém informações de configurações do próprio NetBeans,
esse sistema é chamado de Sistema de Arquivos. Usando convenções, como
arquivos com nome terminado em .instance representam instâncias de classes, e
outros mecanismos semelhantes, para representar objetos JAVA, o NetBeans
armazena uma grande variedade de informações no seu sistema de arquivos. Por
55
exemplo, o sistema de arquivos contém uma pasta chamada /Menu que contém
subpastas com nomes como Arquivo e Editar. Essas subpastas, por sua vez,
contêm arquivos .instante para classes JAVA que implementam ações que
aparecem nos menus Arquivo e Editar. Os módulos são livres para criar suas
próprias pastas no sistema de arquivos do NetBeans e armazenar dados que
interessem para eles, ou adicionar objetos em pastas do NetBeans.
Uma das razões para o NetBeans usar sistema de arquivos é que ele
permite que objetos sejam declarados, mas suas classes não sejam carregadas
pela
JVM
enquanto
não
forem
utilizadas,
não
ocupando
memória
desnecessariamente [GRE 2002]. O sistema de arquivos do NetBeans é o registro
geral para dados e objetos publicamente acessados. Um dos aspectos
importantes do sistema de arquivos virtual do NetBeans é que ele pode disparar
eventos para notificar o resto do sistema quando alguma alteração ocorre. Por
exemplo, o Netbeans é notificado quando ocorrem alterações no sistema de
arquivos e, se foi criado um item de menu, ele mostrará o novo item na barra de
menus.
3.2.7 Camadas dos Módulos
A primeira maneira dos módulos adicionarem suas funcionalidades no
ambiente é através de arquivos, especificamente através de arquivos virtuais
definidos na camada XML do sistema de arquivos. Para isso, existe um pequeno
documento XML nos módulos, que segue o padrão DTD do sistema de arquivos
do NetBeans, declarado no arquivo manifest do módulo.
56
Como a infra-estrutura do NetBeans permite que arquivos sejam mapeados
em instâncias de classes JAVA, o que um módulo geralmente instala são
instâncias de classes, usando a convenção .instance declarada abaixo. Os
arquivos são colocados em pastas no sistema de arquivos que tem significados
conhecidos ou para o sistema ou para o módulo. A camada XML define uma
hierarquia de arquivos e pastas que pode ou não sobrescrever pastas existentes
no sistema de arquivos. Quando um módulo é carregado, este pequeno sistema
de arquivos XML é fundido com o sistema de arquivos do NetBeans, como mostra
a Figura 6.
Figura 6 - Módulo adicionando uma camada XML no sistema de arquivos [GRE 2002].
Freqüentemente um módulo precisa instalar objetos que podem ser
disponíveis para o runtime (como as classes que definem ações para os itens de
menu), ou para outros módulos (por exemplo, serviços, como compilação). O
57
sistema de arquivos provê um caminho para isto ser feito mantendo-se o
acoplamento fraco entre os módulos e o sistema, e assim sendo, nenhum deles
precisa ser programado para isso, tudo é descrito através do XML. Quando um
módulo é removido, sua camada XML é extraída do sistema de arquivos, e os
objetos, itens de menus, serviços e qualquer outra funcionalidade que esse
módulo ofereça simplesmente desaparecerão do sistema.
3.2.8 Camadas no Sistema de Arquivos
Como mencionado anteriormente, uma classe chamada MultiFileSystem
pode representar um conjunto de sistemas de arquivos como se fossem um. O
sistema de arquivos é composto de três camadas distintas, que determinam onde
as configurações são gravadas, essas camadas são detalhadas abaixo:
Padrão ou Global: Este é o MultiFileSystem que contém o conteúdo da subpasta
system/ do diretório onde o NetBeans está instalado. O conteúdo deste sistema de
arquivos é fundido com qualquer sistema de arquivos XML dos módulos que são
empacotados com o NetBeans. As mudanças nesta camada serão propagadas
para todos os usuários.
Sessão ou Usuário: Esta camada contém os diretórios de configuração do
usuário, nela são gravadas informações como as posições das janelas, cores dos
editores, etc. As configurações são específicas para cada usuário e não podem
ser aplicadas globalmente.
58
Projeto: A camada de projeto contém configurações aplicadas ao projeto corrente
(que compreende um conjunto de arquivos, posições de janelas e assim por
diante). É projetada para armazenar ajustes específicos de um determinado
projeto.
Sob esse ponto de vista, o sistema de arquivos poderia ser definido como
um MultiFileSytem que contém outros MultiFileSystems, a Figura 7 detalha esse
conceito e as camadas existentes no sistema de arquivos.
Figura 7 - Estrutura do sistema de arquivos [GRE 2002].
59
4 O Plug-in SVN Proposto
O plug-in SVN proposto pretende se parecer ao máximo, do ponto de vista
de utilização, com o plug-in CVS padrão do NetBeans para tornar mais fácil a
migração dos usuários. Em alguns casos manter essa semelhança não será
possível de devido às diferenças de arquitetura entre os dois sistemas, e em
outros casos, algumas mudanças serão realizadas propositalmente com o intuito
de melhorar características do plug-in CVS.
O plug-in CVS possui um menu na barra de menus que contém os itens
para acessar suas funcionalidades, como descrito na Figura 8.
Figura 8 - Localização do CVS na barra de menus do NetBeans.
As ferramentas de controle de versão deveriam estar localizadas em um
item de um outro menu, como do menu ferramenta, por exemplo. Porém, para
manter o padrão utilizado pelo plug-in CVS, nós criamos um menu SVN que
possuí três itens, um para cara operação implementada, checkout, commit e
update, como mostra a Figura 9.
60
Figura 9 - Localização do SVN na barra de menus do NetBeans.
Optamos por ilustrar as operações com ícones para facilitar o entendimento
das ferramentas pelos usuários leigos.
Foram utilizadas as mesmas figuras
padrão do cliente SVN para Windows e do plug-in SVN para Eclipse, para manter
a padronização e facilidade de utilização.
Figura 10 - Ícones ilustrando as operações SVN.
Ao clicar em um desses itens de menu, o usuário executa a operação
especificada. Para realizar a operação de checkout implementamos um wizard
composto de 2 telas, a primeira onde o usuário insere o servidor ao qual se deseja
61
conectar, o usuário e a senha para a autenticação neste servidor, como detalha a
Figura 11.
Figura 11 - Primeira tela do Wizard de Checkout.
Ao preencher os três campos obrigatórios o botão “Next” é habilitado, e ao
clicar neste botão, o plug-in valida os dados e tenta a conexão com o servidor
especificado utilizando os dados de autenticação fornecidos. Este pode ser um
processo lento pois o servidor fornecido pode ser um servidor localizado na
internet tentando ser acessado por uma conexão com pouca banda, ou, no caso
do servidor não responder, será necessário esperar um timeout de conexão, que
pode durar alguns minutos. Assim, o usuário dever ser avisado que o processo de
conexão está em curso, pois uma tarefa demorada que não retorna um feedback
para o usuário pode ser interpretada como um travamento do sistema. Para este
fim, inserimos uma barra de progressão que indica para o usuário o estado da
tarefa, como vemos na Figura 13.
62
Figura 12 - O preenchimento dos campos obrigatórios habilita o botão Next.
Figura 13 - Barra de progresso indicando a tarefa de conexão em curso.
O usuário pode encerrar a tarefa clicando no botão Stop e encerrando,
assim, a tentativa de conexão. Se um erro ocorrer na tentativa de conexão, será
apresentado ao usuário o motivo do erro da forma padrão dos wizards do
NetBeans, como podemos ver a seguir, na Figura 14.
63
Figura 14 - Exemplo de mensagem de erro na tentativa de conexão.
Se a conexão tiver sucesso, a próxima tela do wizard será apresentada.
Nela o usuário seleciona o item a ser feito checkout, o diretório na máquina local
onde o item será salvo e a versão do item que deseja receber, como podemos ver
na Figura 15.
Figura 15 - Segunda tela do Wizard de Checkout.
64
Para auxiliar o usuário na escolha do item a ser feito checkout e do diretório
onde o item será salvo, duas janelas de auxílio foram implementadas. Elas podem
ser acessadas através dos respectivos botões “Browse...”, como mostra a Figura
16.
Figura 16 - Botões para acesso as janelas de auxílio no Wizard de Checkout.
Ao clicar neste botão a janela de exploração do repositório, ilustrada na
Figura 17, se abrirá. Ela apresentará os itens disponíveis no servidor para
checkout. Segundo [WU 2003], os usuários de um sistema de controle de versão
estão interessados em saber algumas informações específicas ao realizar uma
operação em um repositório de dados, principalmente, “quem” criou um item e
“quando” este item foi criado. Essas informações não são apresentadas no
processo de checkout do plug-in CVS e achamos relevante disponibilizar estas
informações para o usuário neste passo. Assim, o usuário pode escolher o item
que deseja fazer checkout clicando nele e, em seguida em “OK”.
65
Figura 17 - Janela de exploração do repositório.
Ao clicar no outro botão “Browse” uma janela de escolha de diretórios será
exibida. O usuário pode navegar pelos seus diretórios locais e escolher o local em
que salvará os itens que fez checkout. Nesta janela o usuário apenas deve poder
escolher um diretório e não um arquivo, assim, ele só tem acesso aos diretórios.
Feita a escolha, o usuário clica no botão “Open” e a tela é fechada, a janela de
escolha de diretórios é mostrada na Figura 18.
66
Figura 18 - Janela de escolha do diretório a ser feito o checkout.
Por padrão, o usuário realizará o checkout da última versão do item
escolhido, a chamada versão head. Mas, se desejar, o usuário pode clicar radio
box “Other revision” e inserir a versão desejada. O plug-in fornece uma opção de
busca da versão desejada, clicando no botão ”Show revisions”, como mostra a
Figura 16. Ao clicar nesse botão uma janela para pesquisa da versão desejada se
abrirá. Essa janela apresenta as modificações realizadas nessa versão, o autor, a
data e o comentário adicionado, como podemos ver na Figura 19.
67
Figura 19 - Janela de pesquisa das versões de um artefato.
Após o preenchimento dos campos “Module”, “Local Folder” e da escolha
da versão o botão “Finish” será habilitado. Este é o último passo do wizard, ao
clicar no botão “Finish” a operação de checkout será realizada. Esta também pode
ser uma tarefa demorada, pois os itens escolhidos do repositório remoto serão
gravados localmente. Por isto, durante essa tarefa uma barra de progresso
também é mostrada, como ilustra a Figura 20. No plug-in CVS, a janela de wizard
é fechada e uma barra de progresso no canto inferior do IDE é mostrada, porém,
se algum erro ocorrer durante o processo, o usuário não tem a possibilidade de
alterar os dados no wizard. Optou-se por apresentar a barra de progresso na
janela de wizard e só fechá-la no fim da tarefa. Se um erro ocorrer, ele será
68
mostrado para o usuário, vide Figura 21, e o usuário terá a opção de alterar alguns
dados ou cancelar a tarefa.
Figura 20 - Barra de progresso indicando o andamento da tarefa de checkout.
Figura 21 - Exemplo de erro ao executar a operação de checkout.
O usuário tem a possibilidade de interromper a tarefa de checkout através
do botão “Stop”.
69
Ao final do checkout, dois caminhos distintos podem ser tomados. Se o item
recebido do servidor é um projeto NetBeans, será apresentada ao usuário a opção
de abrir esse projeto, como mostra a Figura 22, se não, será apresentada ao
usuário a opção de criar um projeto com os dados recebidos, como pode ser visto
na Figura 23, se ele aceitar, o wizard de criação de projetos do NetBeans será
iniciado.
Figura 22 - O plug-in oferece a possibilidade de abrir um projeto recebido do repositíorio.
Figura 23 - O plug-in fornece a possibilidade de criar um projeto a partir dos dados do
checkout.
Ao clicar no item de menu Commit no menu SVN do NetBeans, o plug-in
varrerá os projetos do IDE e apresentará a janela ilustrada na Figura 24. Essa
janela contém todos os artefatos dos projetos que possuem controle de versão via
Subversion. O usuário seleciona os artefatos que deseja atualizar no repositório e
clica no botão “Commit”. O plug-in, então, realizará a operação de commit para os
artefatos selecionados.
70
Figura 24 - Janela com os artefatos para commit.
Ao clicar no item de menu Update do menu SVN do NetBeans, o plug-in
realizará a operação de update para todos os artefatos que possuem controle de
versão via Subversion e que tem modificações a serem recebidas do repositório.
O status dessa operação é enviado para uma janela de log, ilustrada na Figura 25.
71
Figura 25 - Janela de log das operações.
4.1 O Processo de Desenvolvimento do Plug-in Proposto
O processo de desenvolvimento de software deve ser iterativo e
incremental, guiado por casos de uso e com ênfase na definição da arquitetura
[BOO 96]. Assim, nós identificamos três casos de uso primários que foram
atacados em três ciclos de desenvolvimento, cada ciclo de desenvolvimento
atacou um caso de uso específico. O primeiro ciclo de desenvolvimento resultou
em um protótipo que realizava a operação de checkout, ao fim do segundo ciclo
de desenvolvimento tínhamos um protótipo que realizava as operações de
checkout e commit, e, por fim, após o terceiro ciclo de desenvolvimento, tínhamos
o plug-in finalizado.
Um caso de uso é um documento narrativo que descreve a seqüência de
eventos de um ator (um agente externo) que usa um sistema para completar um
processo [JAC 92], ou seja, descrições narrativas de processos do domínio [LAR
2000]. Com base nos objetivos descritos no primeiro capítulo deste relatório, os
três casos de uso primários identificados foram os casos de uso checkout, commit
e update. Além destes casos de uso primários nós identificamos um caso de uso
72
secundário, o caso de uso startup. Podemos ter uma visão geral dos casos de uso
e dos atores envolvidos através do diagrama de casos de uso da Figura 26.
Figura 26 - Diagrama de casos de uso.
Os casos de uso identificados estão detalhados abaixo:
Caso de uso: Checkout
Atores: Usuário
Tipo: Primário
Descrição: O usuário fornece o repositório SVN desejado, os
dados de acesso a esse repositório e os artefatos de software a
serem
recuperados,
ao
fim,
os
artefatos
solicitados
são
recuperados.
Caso de uso: Commit
Atores: Usuário
Tipo: Primário
73
Descrição: O usuário escolhe os artefatos de software que deseja
enviar ao repositório SVN, ao fim, a operação de commit é
realizada.
Caso de uso: Update
Atores: Usuário
Tipo: Primário
Descrição: O usuário solicita a realização da operação de update,
ao final, a operação de update é realizada.
Com base nesses casos de uso podemos ter um rascunho inicial do modelo
conceitual, como mostra a Figura 27.
Figura 27 – Rascunho inicial do modelo conceitual.
Com base nos casos de uso identificados e no rascunho do modelo
conceitual gerado nós partimos para a execução dos ciclos de desenvolvimento.
74
Cada ciclo de desenvolvimento englobou fases de análise, projeto, implementação
e testes.
Na fase de análise os casos de uso ideais foram refinados gerando os
casos de uso essenciais, que são casos de uso expandidos expressos numa
forma ideal, a qual permanece livre da tecnologia e de detalhes de implementação
[CON 97]. A partir dos casos de uso essenciais foi possível incrementar o modelo
conceitual e desenvolver os diagramas de seqüência do sistema. Com esses
artefatos UML completos pudemos passar para a fase de projeto.
Na fase de projeto foi desenvolvida uma solução lógica baseada no
paradigma orientado a objetos, os casos de uso essenciais da análise deram lugar
aos casos de uso reais, que descrevem o projeto em termos da tecnologia
concreta de entrada e saída e sua implementação geral, os diagramas de
seqüência evoluíram para os diagramas de interação, os quais ilustram como os
objetos devem se comunicar de maneira a entender os requisitos, e o modelo
conceitual serviu de base para a construção do diagrama de classe de projeto,
que sumariza a definição das classes que devem ser implementadas em software.
Na fase de implementação foi gerado código JAVA baseando-se nos
diagramas de colaboração e nos diagramas de classe de projeto, foram realizados
os testes de unidade e os testes de integração. Nos testes de unidades cada
componente do sistema foi testado como se fosse uma caixa preta, testando suas
saídas a partir das entradas. Nos testes de integração foi testado o protótipo de
cada ciclo de desenvolvimento como um todo, testando a utilização do sistema.
O plug-in desenvolvido fez uso da biblioteca Subversion Java (JavaSVN),
que é uma biblioteca desenvolvida em JAVA para auxiliar o acesso a repositórios
75
SVN. A seguir daremos uma breve apresentação desta biblioteca e, em seguida,
descreveremos cada fase de cada ciclo de desenvolvimento apresentando os
artefatos UML gerados.
4.1.1 A Biblioteca Java SVN
A biblioteca Java SVN é uma biblioteca totalmente desenvolvida em Java
que fornece funções para acessar e alterar repositórios Subversion a partir de
aplicações Java. Por ser uma biblioteca totalmente desenvolvida em Java ela não
necessita de qualquer configuração adicional ou executável para rodar em
qualquer sistema operacional que rode Java.
A biblioteca suporta acesso a repositórios via HTTP, HTTPS, SVN e
SVN+SSH e possui uma API que permite acesso de baixo nível aos repositórios.
Entre os projetos que utilizam a biblioteca podemos destacar o Subclipse
(Plug-in Subversion do Eclipse) e o JDeveloper (IDE da Oracle).
4.1.2 Primeiro Ciclo de Desenvolvimento, o Caso de Uso Checkout
Como descrito acima, detalharemos cada atividade realizada neste ciclo de
desenvolvimento, que gerou, ao final, um protótipo que implementa a operação de
checkout.
76
4.1.2.1 A Fase de Análise
Ao expandir o caso de uso ideal checkout chegou-se ao caso de uso
essencial descrito abaixo.
Caso de uso: Checkout
Atores: Usuário
Tipo: Primário e essencial
Descrição: O usuário fornece o repositório SVN desejado, os
dados de acesso a esse repositório e os artefatos de software a
serem
recuperados,
ao
fim,
os
artefatos
solicitados
são
recuperados.
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
checkout.
2. O usuário registra o repositório 3. Mostra os artefatos de
SVN desejado e os dados de software disponíveis neste
acesso a esse repositório.
repositório e as informações
de versão destes artefatos.
4. O usuário registra o artefato e
a versão de software que deseja
receber do repositório.
5. Acrescenta a informação do
artefato à operação corrente e
apresenta ao usuário o
artefato e a versão escolhida.
6. O usuário registra o caminho
(PATH) onde serão colocados os
artefatos escolhidos.
7. O usuário indica que já
registrou todas as informações.
8.
Realiza
a
operação
solicitada e, ao fim, apresenta
uma mensagem de sucesso.
77
Com base neste caso de uso essencial foi gerado o diagrama de seqüência
apresentado na Figura 28.
Figura 28 - Diagrama de seqüência do caso de uso checkout.
Após esses passos o modelo conceitual da análise preliminar foi expandido
gerando o modelo conceitual da Figura 29. Este modelo conceitual traz a inserção
do conceito “NetBeans” que é responsável por dar início ao plug-in. Foram
também adicionados os conceitos de “especificação de artefato”, que serve para
reduzir informação redundante sobre os artefatos de software e de “artefatos da
operação”, que diz respeito aos artefatos envolvidos na operação corrente.
78
Figura 29 - Modelo conceitual expandido.
A fase de análise enfatiza uma compreensão dos requisitos, dos conceitos
e das operações relacionados com um sistema, assim, após a criação destes três
artefatos UML, o processo de desenvolvimento encontrava-se maduro o suficiente
para partir à fase de projeto, que será descrita a seguir.
4.1.2.2 A Fase de Projeto
Na fase de projeto é desenvolvida uma solução lógica baseada no
paradigma orientado a objetos. O coração desta solução é a criação de diagramas
de interação, os quais ilustram como os objetos devem se comunicar de maneira a
atender os requisitos.
79
Porém, antes dos diagramas de interação devemos definir os casos de uso
reais. Um caso de uso real descreve o projeto real do caso de uso em termos da
tecnologia concreta de entrada e saída e sua implementação geral. Eles são
criados a partir dos casos de uso essenciais definidos na análise, podemos
verificar o caso de uso real checkout a seguir. Este caso de uso leva em
consideração as figuras: Figura 30, Figura 31 e Figura 32. O layout dessa janela
foi escolhido tendo em vista a compatibilidade com o plug-in CVS do NetBeans,
como discutido no início deste capítulo.
Figura 30 - Solicitando a realização do checkout.
80
Figura 31 - Janela 1.
Figura 32 - Janela 2.
Caso de uso: Checkout
Atores: Usuário
Tipo: Primário e real
81
Descrição: O usuário fornece o repositório SVN desejado, os
dados de acesso a esse repositório e os artefatos de software a
serem
recuperados,
ao
fim,
os
artefatos
solicitados
são
recuperados.
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
checkout clicando em A da Figura
30.
2. O usuário registra o repositório
SVN desejado em A da Janela 1,
o usuário de acesso em B e a
senha em C.
3. O usuário confirma os dados
fornecidos clicando em D da
Janela 1.
4. O usuário registra o artefato
em A da Janela 2 e a versão de
software que deseja receber do
repositório em D e E, ou, em C,
se ele deseja receber a última
versão.
6. O usuário registra o caminho
(PATH) onde serão colocados os
artefatos escolhidos em B.
7. O usuário indica que já
registrou todas as informações
clicando em I.
8.
Realiza
a
operação
solicitada e, ao fim, apresenta
uma mensagem de sucesso,
demonstrada na Figura 23.
Seqüências alternativas
Linha 4: O usuário clica em F da Janela 2 e escolhe o artefato
desejado na janela ilustrada na Figura 17. Ao fim, o plug-in
apresenta o artefato escolhido em A da Janela 2.
82
Linha 6: O usuário clica em G da Janela 2 e escolhe, na janela
ilustrada na Figura 18, o caminho onde deseja salvar os artefatos
solicitados no checkout. Após o usuário escolher o caminho
desejado o plug-in apresenta o caminho escolhido em B da Janela
2.
Linha 8: O usuário pode criar um projeto do Netbeans com os
artefatos que foram adquiridos selecionando essa opção na janela
ilustrada pela Figura 23. Ao selecionar essa opção o Wizard para
criação de novos projetos do Netbeans aparecerá.
O próximo passo realizado foi a criação dos diagramas de interação. O
UML define dois tipos de diagramas de interação, a saber, os diagramas de
colaboração e os diagramas de seqüência. Optou-se por utilizar diagramas de
colaboração por sua capacidade de expressão, sua habilidade para expressar
mais informações contextuais e da sua relativa economia de espaço. Cada
operação do sistema, detalhadas no diagrama de seqüência, resultou em um
diagrama de colaboração, como podemos ver nas figuras Figura 33, Figura 34,
Figura 35 e Figura 36.
83
Figura 33 - Diagrama de colaboração de registrarRepositório.
Figura 34- Diagrama de colaboração de registrarArtefato.
Figura 35 - Diagrama de colaboração de registrarCaminho.
Figura 36 - Diagrama de colaboração de terminarOperação.
84
Algumas decisões importantes de projeto foram feitas nessa etapa. Nós
poderíamos ter optado pelo padrão Fachada (Façade) para construir o controlador
das mensagens do sistema tendo em vista que existem poucas operações de
sistema. Porém, como a idéia do plug-in é que ele se expanda e implemente
várias operações SVN essa classe acabaria recebendo muitas responsabilidades
e perdendo coesão. Assim, nós optamos por ter controladores de caso de uso,
representado, neste caso, pela classe CheckoutController.
Podemos ver na Figura 33 que surgiram as classes SVNRepositoryFactory
e ISVNAuthenticationManager. Essas classes pertencem a biblioteca JavaSVN e
são necessárias para realizar o acesso aos repositórios Subversion. A classe
SVNRepositoryFactory oferece o serviço de criar um SVNRepository a partir de
uma SVN URL. Esta URL contém o tipo de acesso que será utilizado pelo usuário,
podendo
ser,
HTTP,
HTTPS,
SVN
ou
SVN+SSH,
e,
assim,
a
SVNRepositoryFactory pode criar um SVNRepository que atenda ao requisito de
acesso
requerido
pelo
usuário.
A
classe
ISVNAuthenticationManager
é
responsável por realizar a autenticação no repositório.
Assim, com base nos diagramas de colaboração e no modelo conceitual foi
criado o diagrama de classes de projeto, apresentado na Figura 37.
85
Figura 37 - Diagrama de Classes de Projeto.
A maioria das associações entre os objetos do diagrama de classes de
projeto representa visibilidade por atributo, porém três dessas associações
diferenciam-se das demais, são elas, o relacionamento entre CheckoutOperation e
SVNRepositoryFactory,
SVNRepository
e
o
o
relacionamento
relacionamento
entre
entre
SVNRepositoryFactory
e
CheckoutOperation
e
ISVNAuthenticationManager.
Normalmente o criador de um objeto requer uma conexão permanente com
o objeto que ele criou, porém, como o objeto responsável por criar um
SVNRepository segue o padrão Factory, isso não é necessário. Assim, o
relacionamento entre SVNRepositoryFactory e SVNRepository não representa
86
uma visibilidade por atributo, mas sim declarada localmente, como também o
relacionamento entre CheckoutOperation e ISVNAuthenticationManager.
Com a finalização do caso de uso real, dos diagramas de colaboração e
dos diagramas de classes de projeto, havia detalhes suficientes para gerar código
para a camada de objetos de domínio. Partimos, então para a terceira fase deste
ciclo de desenvolvimento, a fase de implementação.
4.1.2.3 A Fase de Implementação
O objetivo da fase de implementação é mapear artefatos de projeto para
código, em uma linguagem orientada a objetos. Para reduzir os riscos e aumentar
as chances de desenvolver uma aplicação adequada, o desenvolvimento deveria
estar baseado em um volume significativo de trabalho em modelagem de análise e
de projeto, antes de começar a codificação. Porém, mesmo assim, a fase de
implementação não é apenas um processo de tradução relativamente mecânico,
muito pelo contrário, os resultados gerados durante o projeto são o primeiro passo
incompleto durante a programação e o teste.
Iniciaremos este tópico descrevendo os passos realizados para criar o
projeto do plug-in proposto no NetBeans e em seguida passaremos a descrever os
passos realizados na implementação do caso de uso checkout.
Para auxiliar a criação de plug-ins o NetBeans oferece, no wizard de
criação de novos projetos (que pode ser visto na Figura 38), a categoria NetBeans
Plug-in Modules que fornece a opção de criação de três tipos de projetos,
descritos a abaixo:
87
Module Project – Cria um projeto para o desenvolvimento de um plug-in
autônomo.
Library Wrapper Module Project – Cria um wrapper para uma biblioteca usada
por algum plug-in.
Module Suite Project – Cria um projeto contendo um conjunto interdependente
de plug-ins e wrappers de bibliotecas que pode ser usado para deployment.
Figura 38 - Wizard de criação de projetos do NetBeans.
Como o plug-in proposto faz uso da biblioteca JAVA SVN, fez-se necessário
primeiramente a criação de uma suíte que conteria o wrapper da biblioteca e o
plug-in. A criação da suíte é bem simples, bastando indicar, no wizard de criação,
88
o nome da suíte, o diretório onde ela será salva e a plataforma NetBeans em que
ela irá funcionar.
Após a criação da suíte foi criado um wrapper para a biblioteca JAVA SVN.
A criação de wrappers também é bem simples, bastando indicar, no wizard de
criação, o nome do wrapper, a localização do arquivo .jar da biblioteca, o diretório
onde ele será salvo e a suíte a que ele será adicionado.
Por fim, foi criado o projeto do plug-in. No wizard de criação de módulos
autônomos foi indicado o nome do módulo, o diretório onde ele será salvo, a suíte
a que ele pertence e os parâmetros Localizing Bundle, que indica um arquivo
utilizado para internacionalização, e XML Layer, que indica um arquivo XML onde
são registrados itens como menus, barras de tarefas e botões no sistema de
arquivos do NetBeans.
Podemos conferir o menu de projetos do NetBeans na Figura 39 onde
primeiramente é apresentado o wrapper, que recebeu o nome de “javasvn”, logo
após podemos conferir o projeto do plug-in, que recebeu o nome de
“SubversionModule”, e, por último, a suíte denominada “SubversionSuite”.
89
Figura 39 - Menu de projetos no NetBeans.
Após a criação dos projetos partimos para a implementação da interface
gráfica. O primeiro passo foi a adição do menu SVN na barra de menus do
NetBeans. Para a criação de menus e itens de menus o NetBeans oferece a
categoria NetBeans Module Development no wizard de criação de novos arquivos.
Nesta categoria escolhemos o tipo de arquivo Action para criar itens de menu,
como mostra a Figura 40.
90
Figura 40 - Wizard de criação de novos arquivos.
Este wizard edita o arquivo XML Layer e cria uma classe que estende
CallableSystemAction, assim, quando o usuário clica no item de menu criado, o
método performAction desta classe é chamado. Podemos conferir algumas linhas
do arquivo XML Layer criado na Figura 41.
Figura 41 - Um trecho do arquivo XML Layer criado.
91
Como pode ser conferido, a tag folder chamada Menu indica a barra de
menus do NetBeans, abaixo dela, foi criada a tag SVN, que indica um menu
apresentado na barra de menus e, logo abaixo dela, foram inseridos os itens de
menus desejados. Neste arquivo foram inseridos, também, os separadores de
menus e as classes responsáveis por responder as ações dos usuários nos
menus. Com esses passos realizados, o ambiente de desenvolvimento do plug-in
estava finalizado e foi possível partir para a implementação do caso de uso
checkout.
A primeira tarefa da implementação do caso de uso checkout foi a criação
do wizard de checkout. Para a criação de wizards o NetBeans oferece o tipo de
arquivo wizard na janela de criação de novos arquivos. A criação de wizards tornase muito simples, bastando o usuário escolher o nome do wizard e o número de
painéis que ele conterá. Ao final, o NetBeans cria um arquivo .java e outro .form
para cada painel do wizard criado e um arquivo sampleAction.java que contém um
exemplo de como utilizar o wizard criado.
O NetBeans oferece dois tipos de validação para os painéis, uma síncrona
e outra assíncrona. Por padrão o NetBeans cria os painéis do wizard
implementado a classe WizardDescriptor.ValidatingPanel, que fornecem validação
síncrona, assim, quando o usuário clica no botão Next ou Finish do wizard o
método validate() do painel corrente é chamado.
Porém, os dois painéis utilizados no wizard checkout necessitavam de
validação assíncrona, o primeiro pois precisava esperar a realização da conexão
com o repositório e o segundo pois precisava esperar a realização da operação,
92
assim
tivemos
fazer
com
que
os
painéis
WizardDescriptor.AsynchronousValidatingPanel,
implementassem
utilizada
para
a
classe
validações
assíncronas. Esta interface contém o método prepareValidation() que é chamado
de maneira síncrona quando o botão Next ou Finish do wizard é clicado e o
método validate(), que é chamado em uma thread separada quando algum desses
botões são clicados. Durante a validação o botão Cancel é utilizado para enviar
um sinal de interrupt() para a thread de validação e os botões Next e Finish são
desabilitados, em caso de sucesso o wizard passa para o próximo painel ou, se for
o caso, finaliza.
Após a criação do wizard de checkout foram implementadas as classes do
diagrama de classes, que podem ser visualizadas no Anexo 2, finalizando assim a
fase de implementação do caso de uso checkout.
4.1.3 Segundo Ciclo de Desenvolvimento, o Caso de Uso Commit
Serão descritas, a seguir, as atividades desenvolvidas no segundo ciclo de
desenvolvimento realizado, que atacou o caso de uso commit. Descreveremos
essas atividades com um grau menor de detalhes dos artefatos UML gerados, que
seguem o padrão realizado no caso de uso checkout, porém, todos os artefatos
UML gerados podem ser encontrados no Anexo 1.
4.1.3.1 A Fase de Análise
O plug-in CVS do NetBeans oferece a possibilidade da realização da
operação de commit por projeto ou um commit abrangente que varre todos os
93
projetos. Nós decidimos implementar, no escopo deste trabalho, a segunda
alternativa, ou seja, um commit que varra os projetos em busca de artefatos que
contêm alterações a serem enviadas ao repositório. Para tal propósito expandimos
o caso de uso ideal descrito no tópico 4.1 e geramos o caso de uso expandido
apresentado abaixo.
Caso de uso: Commit
Atores: Usuário
Tipo: Primário e essencial
Descrição: O usuário escolhe os artefatos de software que deseja
enviar ao repositório SVN, ao fim, a operação de commit é
realizada.
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
Commit.
2. O usuário registra uma
mensagem
descrevendo
a
operação de commit.
3. Mostra os artefatos de
software
disponíveis
para
commit.
4. O usuário seleciona os
artefatos que deseja atualizar no
repositório.
5.
O
usuário
selecionou todos
desejados.
indica
que
os artefatos
6. Realiza a operação.
94
Para este caso de uso foram identificadas as operações do sistema:
adicionarMensagem, adicionarArtefato e terminarOperação. Não foi observada a
necessidade de adição de nenhum conceito novo ao diagrama conceitual, que,
assim, permaneceu inalterado.
4.1.3.2 A Fase de Projeto
O primeiro passo da fase de projeto foi a criação do frame de commit, que é
chamado a partir do item de menu commit, como descrito no tópico 4.1. Esse
frame tem o diferencial, em relação ao frame de commit do plug-in CVS, de
oferecer a possibilidade do usuário selecionar os artefatos que deseja enviar ao
repositório, como foi contemplado no caso de uso essencial desenvolvido na
análise.
A partir da criação desta interface passamos para a elaboração do caso de
uso real, que pode ser contemplado a seguir:
Caso de uso: Commit
Atores: Usuário
Tipo: Primário e real
Descrição: O usuário escolhe os artefatos de software que deseja
enviar ao repositório SVN, ao fim, a operação de commit é
realizada.
Seqüência Típica de Eventos
95
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
commit clicando em A da Figura
42.
2. O usuário registra uma
mensagem
descrevendo
a
operação de commit em B da
Figura 42.
3. Mostra os artefatos de
software
disponíveis
para
commit em D da Figura 42.
4. O usuário seleciona os
artefatos que deseja atualizar no
repositório em C da Figura 42.
5.
O
usuário
indica
que
selecionou todos os artefatos
desejados clicando em E da
Figura 42.
6. Realiza a operação.
Figura 42 - Janela da operação de commit.
96
Após a elaboração do caso de uso real acima foram criados os diagramas
de
colaboração.
O
principal
diagrama
de
colaboração
deste
ciclo
de
desenvolvimento foi o diagrama de colaboração de adicionarArtefato, que pode
ser visto na Figura 43. Nesse diagrama surgiu a classe ProjectCatalog, que é
responsável por solicitar ao NetBeans os projetos abertos e verificar se esses
projetos são controlados pelo SVN.
Figura 43 - Diagrama de colaboração de adicinarArtefato.
4.1.3.3 A Fase de Implementação
Na fase de implementação foi criada, primeiramente, a janela da operação
de commit. Esta janela possui uma tabela que apresenta os artefatos que estão
disponíveis para commit, oferecendo a possibilidade de o usuário selecionar os
artefatos que deseja através de um JCheckBox. Além disso, a tabela disponibiliza
ao usuário o ícone, o status e o repositório do artefato, o que faz com que ela não
possa ser construída com um simples JTable. Para a construção desta tabela
tivemos de criar um AbstractTableModel para definir o renderizador e o editor de
cada célula, um TableCellRenderer, para renderizar a coluna File de modo que ela
97
apresente um JCheckBox e um JLabel, e um AbstractCellEditor para possibilitar a
edição da coluna File. Além disso, tínhamos de implementar a comunicação entre
o
AbstractTableModel
e
a
janela,
e
o
fizemos
através
do
padrão
Publish/Subscriber, onde a janela faz o papel de Subscriber, que recebe os
eventos da tabela.
Depois de desenvolvida a janela da operação de commit partimos para a
criação da classe ProjectCatalog. Essa classe faz uso da classe OpenProjects,
encontrada no pacote “org.netbeans.api.project.ui” da API do NetBeans, através
do método getOpenProjects, para solicitar os projetos abertos. Uma vez
conhecendo os projetos abertos a classe usa SVNDirectory, encontrada no pacote
org.tmatesoft.svn.core.internal.wc da bilbioteca JavaSVN, para encontrar os
arquivos, de cada projeto, disponíveis para commit.
Por fim, implementamos as classes CommitController, que faz a interface
entre a camada de apresentação e a camada de domínio, e a classe
CommitOperation, que realiza a operação de commit.
4.1.4 Terceiro Ciclo de Desenvolvimento, o Caso de Uso Update
Este ciclo de desenvolvimento foi o ciclo mais curto do projeto pois o caso
de uso update realiza pouca interação com o usuário, diminuindo, assim, o
número de interfaces a serem desenvolvidas.
Descreveremos esse ciclo de desenvolvimento com o mesmo nível de
detalhes utilizados na descrição do segundo ciclo de desenvolvimento.
98
4.1.4.1 A Fase de Análise
Implementamos a operação de update da mesma maneira que o plug-in
CVS do NetBeans realiza a operação Update All Files, ou seja, o usuário solicita a
operação de update e o plug-in realiza a operação para todos os arquivos de
todos os projetos abertos na IDE. Com esse objetivo expandimos o caso de uso
ideal update para o caso de uso essencial detalhado a seguir.
Caso de uso: Update
Atores: Usuário
Tipo: Primário e essencial
Descrição: O usuário solicita a realização da operação de update,
ao final, a operação de update é realizada.
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
Update.
2. Realiza a operação de
Update.
Foi
identificado,
pra
este
caso
de
uso,
apenas
a
operação
realizarOperação.
4.1.4.2 A Fase de Projeto
Para este caso de uso não foi desenvolvida nenhuma janela. Ao clicar no
item de menu “Update All Files” do menu SVN, detalhado na Figura 9 no início do
99
capítulo 4, o plug-in realiza a operação de update. Podemos conferir o diagrama
de colaboração de realizarOperação na Figura 44.
Figura 44 - Diagrama de colaboração de realizarOperação.
Como podemos ver no diagrama de colaboração acima o UpdateController
solicita ao ProjectCatalog os arquivos para update e os envia para o
UpdateOperation, que realiza a operação.
4.1.4.3 A Fase de Implementação
A fase de implementação do terceiro ciclo de desenvolvimento constituiu-se
da implementação das classes UpdateController, que faz a interface entre a
camada de apresentação e a camada de domínio, a implementação da classe
UpdateOperation, que é responsável pela realização da operação de update e a
implementação do método getUpdateDevices na classe ProjectCatalog, que
obtém todos os arquivos, de todos os projetos, disponíveis para update utilizando
as mesmas classes descritas na fase de implementação do segundo ciclo de
desenvolvimento.
100
5 Conclusões
A implementação de um plug-in para um IDE é uma tarefa complexa e que
exige constante interação com as APIs do ambiente e revisão da documentação.
Devido a esta complexidade, o ambiente NetBeans fornece alguns Wizards com o
intuito de acelerar o processo de desenvolvimento.
Desenvolver um artefato de software que forneça as funções de um sistema
de controle de versão é uma tarefa ampla e que demanda uma boa quantidade de
tempo e trabalho, a utilização da biblioteca JavaSVN diminuiu consideravelmente
essas necessidades.
O plug-in desenvolvido foi colocado para testes no ambiente de trabalho
corporativo da empresa Suntech Telecom Solutions e foi homologado satisfazendo
os requisitos de desempenho e usabilidade. Logo, ele será disponibilizado no site
sourceforge.net, que é um repositório de projetos open-source.
Acreditamos que o objetivo do trabalho foi alcançado pois desenvolvemos
uma ferramenta que implementa as operações delimitadas no escopo do trabalho,
que é semelhante, do ponto de vista de utilização, e compatível com o plug-in CVS
do NetBeans, que disponibiliza acesso aos repositórios através dos protocolos
SVN, SVN sobre SSH e HTTP, que é de fácil utilização e que traz algumas
melhorias, como, por exemplo, a apresentação, na tela de escolha do projeto a ser
feito checkout, do usuário e versão referente a cada projeto, que é uma
informação normalmente necessária ao usuário, como comentada no capítulo 4, e
a possibilidade da escolha da versão do artefato nesta mesma tela.
101
5.1 Sugestões para Trabalhos Futuros
Para trabalhos futuros sugerimos a implementação das demais operações
existentes nos sistemas de controle de versão, como a criação de repositórios e
as operações de diff, report e get.
102
6 Referências Bibliográficas
[BOO 96] BOOCH, G. Object Solutions: Managing the Object-Oriented Project.
Menlo Park: Addison-Wesley, 1996.
[BRO 92a] BROWN, A.; EARL, A.; MCDERMID, J. Software Engineering
Environments: Automated Support for Software Engineering. London: McGrawHill, 1992.
[BRO 92b] BROWN, A.; MCDERMID, J. Learning from IPSE’s mistakes. v. 9 Los
Alamitos: IEEE Software, 1992.
[BRO 92c] BROWN A.; FEILER P.; WALLNAU K. Past and future models of CASE
integration, Proc. of the 5th International Workshop on Computer-Aided Software
Engineering, IEEE, Julio 1992.
[CHR 95] CHRISTIE, A.. Software Process Automation: The Technology and its
adoption. Berlin: Springer Verlag, 1995.
[CON 94] CONRADI, R., et al. EPOS: Object-Oriented Cooperative Process
Modeling. In: FINKELSTEIN, A. et al. (Ed.). Software Process Modeling and
Technology. Taunton: Research Studies Press, 1994. p. 33-70.
[CON 97] CONSTANTINE, L. 1997. The Case for Essential Use Cases. Object
Magazine May 1997. NY, NY: SIGS Publications.
[CVS 2006] CVS Website, http://www.cvshome.org/, 2006.
[DAR 91] DART S. "Concepts in Configuration Management Systems," presented
at the3rd International workshop on software configuration management,
Trondheim, Norway, 1991.
103
[DOW 91] DOWSON, M.; NEJMEH, B.; RIDDLE, W.. Fundamental Software
Process Concepts. In: EUROPEAN WORKSHOP ON SOFTWARE PROCESS
MODELLING, 1., 1991, Milan, Italy. Proceedings... [S.l.]: AICA Press, Maio 1991.
[FEI 93] FEILER, P.; HUMPHREY, W.. Software Process Development and
Enactment: Concepts and Definitions. In: INTERNATIONAL CONFERENCE ON
THE SOFTWARE PROCESS, 2., 1993, Berlin. Proceedings... Berlin: IEEE
Computer Society Press, Março 1993.
[GRE 2002] GREENE S. NetBeans: The Definitive Guide. Sebastopol: O’Reilly &
Associates, 2002.
[HUM 89b] HUMPHREY, S. Managing the Software Process. New York:
Addison-Wesley, 1989.
[JAC 92] JACOBSON, I., et al. Object-Oriented Software Engineering: A Use
Case Driven Approach. Reading: Addison-Wesley, 1992.
[JEN 2005] JENSEN C.; SCACC W. Collaboration, Leadership, Control, and
Conflict Negotiation and the Netbeans.org Open Source Software
Development Community. Irvine: University of California, 2005.
[KAP 2005] KAPPEL G., et al. ModelCVS A Semantic Infrastructure for Modelbased Tool Integration. Business Informatics Group, Vienna University of
Technology, 2005.
[LARS 95] BENDIX, L. Configuration Management and Version Control Revisited.
Institute for Electronic Systems, Aalborg University Denmark, Dec. 1995.
[LAR 2000] LARMAN, C. Utilizando UML e padrões: uma introdução à análise e
ao projeto orientados a objetos, trad. Luiz A. Meirellies Salgado. Porto Alegre:
Bookman, 2000.
[LIM 98] LIMA, C. Um Gerenciador de Processos de Software para o Ambiente
PROSOFT. 1998. 197 f.. Dissertação (Mestrado em Ciências da Computação) –
Instituto de Informática, Universidade Federal do Rio Grande do Sul, Porto Alegre,
1998.
104
[LON 93] LONCHAMP, J. A Structured Conceptual and Terminological Framework
for Software Process Engineering. In: INTERNATIONAL CONFERENCE ON THE
SOFTWARE PROCESS, 2., 1993, Berlin. Proceedings... Berlin: IEEE Computer
Society Press, Março 1993.
[NET 2006] NetBeans website, http://www.netbeans.org/, 2006.
[OST 87] OSTERWEIL, L. Software Processes are Software Too. In:
INTERNATIONAL CONFERENCE ON SOFTWARE ENGINEERING, 9., 1987,
Monterey, California. Proceedings... Monterey: IEEE Computer Society Press,
1987.
[PER 2006] Perforce website, http://www.perforce.com/, 2006
[PRE 95] PRESSMAN, R. S. Engenharia de Software. São Paulo: Makron Books,
1995.
[RAT 2006] Rational ClearCase website,
http://www.rational.com/products/clearcase/index.jsp, 2006.
[SVN 2006] SVN Website, http://subversion.tigris.org/, 2006.
[TIC 85] TICHY W. F. RCS - A System for Version Control: Software - Practice
and Experience, vol. 15, no. 7, pp. 637-654, 1985.
[THO 92] THOMAS, L.; NEJMEH, A. Definitions of Tool Integration for
Environments. Los Alamitos: IEEE Software, Março 1992, v. 9, n. 2., p. 29-35.
[WOR 2005] WORTH D. J.; GREENOUGH C. Comparison of CVS and
Subversion. Didcot: Oxfordshire, 2005.
105
[WU 2003] WU, X. Visualization of Version Control Information, Wuhan University
of Hydraulic and Electric Engineering, 2003.
106
Anexo 1 – Artigo
Integração do Sistema de Controle de Versão Subversion (SVN)
com o IDE NetBeans
Lucio Moratelli Prado
Sistemas de Informação, Departamento de Informática e estatística – INE
Universidade Federal de Santa Catarina (UFSC), Brasil
[email protected]
Resumo
Este
breve
artigo
apresenta
explanação
uma
do
NetBeans,
Palavras-Chaves:
Subversion, ambiente integrado de
desenvolvimento de um plug-in que
desenvolvimento
implementa a integração entre um
gerencia de configuração, controle de
dos
versão, plug-in.
ambientes
integrados
de
de
software,
desenvolvimento de software mais
utilizados para desenvolvimento de
Introdução
software JAVA, o NetBeans, com
A
uma
ferramenta
da
qualidade de software tem movido a
versão que vem ganhando espaço na
área de Engenharia de Software nos
comunidade
últimos anos. Como um dos maiores
Open
controle
busca
de
Subversion.
de
crescente
Source,
o
obstáculos desta busca podemos
107
citar a grande complexidade das
ferramentas utilizadas no processo de
aplicações
atuais,
desenvolvimento de software.
necessária
a
que
tornou
automatização
do
Assim, propomos a integração
processo de desenvolvimento para
da ferramenta de controle de versão
garantir o controle e a gerência
Subversion com o IDE Netbeans.
efetivos do processo de software [LIM
98].
Motivação
Em
necessidade
resposta
a
surgiram
essa
A NetBeans.org é atualmente
ferramentas
uma das maiores comunidades de
individuais de apoio à programação,
desenvolvimento
como compiladores, montadores e
código fonte aberto do mundo. O uso
depuradores
seguida,
da ferramenta de controle de versão
ferramentas de apoio a outras fases
Subversion vem crescendo e ela
do
software,
tende a tornar-se o padrão de facto
como as ferramentas de controle de
para este fim na comunidade Open
versão.
Source.
e,
em
desenvolvimento
Porém
o
uso
de
de
várias
O
ferramentas
de
fato
tão
software
de
dessas
duas
utilizadas
não
ferramentas acabou por acrescentar
possuírem integração nos motivou a
mais complexidade ao processo de
promovê-la.
desenvolvimento de software. Assim,
surgiram os ambientes integrados de
desenvolvimento de software (IDE),
que se propõe a integrar as diversas
O Plug-in Proposto
O
plug-in
SVN
proposto
pretende se parecer ao máximo, do
108
ponto de vista de utilização, com o
plug-in CVS padrão do NetBeans
para tornar mais fácil a migração dos
usuários. Em alguns casos manter
essa semelhança não será possível
de
devido
as
diferenças
de
arquitetura entre os dois sistemas, e
em outros casos, algumas mudanças
serão realizadas propositalmente com
o intuito de melhorar características
do plug-in CVS.
Para
operação
de
realizarmos
uma
Checkout
basta
clicarmos no item de menu Checkout.
Ao clicarmos neste ícone o wizard de
Podemos
ter
acesso
as
operações do Subversion através do
menu SVN, localizado na barra de
menus do NetBeans. Neste menu
encontramos três itens de menu,
checkout se abrirá. Na primeira etapa
deste wizard inserimos o repositório
no qual encontram-se os artefatos
que desejamos, o usuário e a senha
para login neste repositório.
referentes as opções de Commit,
Update e Checkout. As operações de
Commit e Update só são habilitadas
quando
existe
controle
de
um
versão
projeto
através
com
do
Subversion.
109
Após
inserirmos
essas
repositório, o diretório da máquina
prosseguir
local onde esse artefato será salvo e
com a operação clicando no botão
a versão do artefato que desejamos
Next.
adquirir.
informações
podemos
Neste
momento
os
dados
inseridos são validados e é realizada
a
conexão
com
o
repositório
fornecido. Podemos acompanhar o
processo de conexão através de uma
barra
de
progresso,
e
temos,
também, a possibilidade de para esse
processo
a
qualquer
momento
Após
a
inserção
dessas
informações clicamos em “Finish”
para terminar a operação.
clicando no botão Stop.
Para a realização da operação
de commit clicamos no item de menu
Commit do menu SVN. O plug-in irá
apresentar
uma
janela
com
os
artefatos que possuem controle de
Caso a conexão ocorra com
sucesso
operação
a
segunda
de
etapa
checkout
nos
da
então, os artefatos que desejamos
á
em atualizar no repositório e clicamos
apresentada.
Na
versão via Subversion. Escolhemos,
no botão Commit.
segunda
etapa
da
operação de checkout indicamos o
artefato que desejamos receber do
110
proposta
atingiu
disponibilizando
aos
seu
objetivo,
usuários
do
NetBeans a possibilidade de desfrutar
dos benefícios do sistema de controle
de
verões
Subversion
com
a
facilidade e a comodidade que a
integração
Para a realização da operação
de update clicamos no item de menu
Update do menu SVN. O plug-in
varrerá
todos
os
projetos
do
NetBeans e realizará a operação de
update para todos os artefatos que
dessas
ferramentas
proporciona.
Referências
[CHR 95] CHRISTIE, A.. Software
Process
Automation:
The
Technology and its adoption. Berlin:
Springer Verlag, 1995.
possuem controle de versão via
Subversion
e
que
possuem
alterações a serem recebidas do
repositório.
Conclusão
Através do plug-in proposto a
integração do IDE NetBeans com o
sistema
de
Subversion
acreditamos
controle
foi
de
versão
realizada.
Assim,
que
a
integração
[CON 94] CONRADI, R., et al. EPOS:
Object-Oriented Cooperative Process
Modeling. In: FINKELSTEIN, A. et al.
(Ed.). Software Process Modeling and
Technology.
Taunton:
Research
Studies Press, 1994. p. 33-70.
[CON 97] CONSTANTINE, L. 1997.
The Case for Essential Use Cases.
Object Magazine May 1997. NY, NY:
SIGS Publications.
[DOW 91] DOWSON, M.; NEJMEH,
B.; RIDDLE, W.. Fundamental
Software Process Concepts. In:
EUROPEAN
WORKSHOP
ON
SOFTWARE
PROCESS
MODELLING, 1., 1991, Milan, Italy.
111
Proceedings...
Maio 1991.
[S.l.]:
AICA
Press,
[FEI 93] FEILER, P.; HUMPHREY,
W.. Software Process Development
and Enactment: Concepts and
Definitions. In: INTERNATIONAL
CONFERENCE
ON
THE
SOFTWARE PROCESS, 2., 1993,
Berlin. Proceedings... Berlin: IEEE
Computer Society Press, Março 1993.
[HUM
89b]
HUMPHREY,
S.
Managing the Software Process.
New York: Addison-Wesley, 1989.
112
Anexo 2 – Artefatos UML
Casos de uso ideais:
Caso de uso: Checkout
Atores: Usuário
Tipo: Primário
Descrição: O usuário fornece o repositório SVN desejado, os
dados de acesso a esse repositório e os artefatos de software a
serem
recuperados,
ao
fim,
os
artefatos
solicitados
são
recuperados.
Caso de uso: Commit
Atores: Usuário
Tipo: Primário
Descrição: O usuário escolhe os artefatos de software que deseja
enviar ao repositório SVN, ao fim, a operação de commit é
realizada.
Caso de uso: Update
Atores: Usuário
Tipo: Primário
Descrição: O usuário solicita a realização da operação de update,
ao final, a operação de update é realizada.
Casos de uso essenciais:
Caso de uso: Checkout
Atores: Usuário
Tipo: Primário e essencial
113
Descrição: O usuário fornece o repositório SVN desejado, os
dados de acesso a esse repositório e os artefatos de software a
serem
recuperados,
ao
fim,
os
artefatos
solicitados
são
recuperados.
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
checkout.
2. O usuário registra o repositório 3. Mostra os artefatos de
SVN desejado e os dados de software disponíveis neste
acesso a esse repositório.
repositório e as informações
de versão destes artefatos.
4. O usuário registra o artefato e
a versão de software que deseja
receber do repositório.
5. Acrescenta a informação do
artefato à operação corrente e
apresenta ao usuário o
artefato e a versão escolhida.
6. O usuário registra o caminho
(PATH) onde serão colocados os
artefatos escolhidos.
7. O usuário indica que já
registrou todas as informações.
8.
Realiza
a
operação
solicitada e, ao fim, apresenta
uma mensagem de sucesso.
Caso de uso: Commit
Atores: Usuário
Tipo: Primário e essencial
Descrição: O usuário escolhe os artefatos de software que deseja
enviar ao repositório SVN, ao fim, a operação de commit é
realizada.
114
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
Commit.
2. O usuário registra uma
mensagem
descrevendo
a
operação de commit.
3. Mostra os artefatos de
software
disponíveis
para
commit.
4. O usuário seleciona os
artefatos que deseja atualizar no
repositório.
5.
O
usuário
selecionou todos
desejados.
indica
que
os artefatos
6. Realiza a operação.
Caso de uso: Update
Atores: Usuário
Tipo: Primário e essencial
Descrição: O usuário solicita a realização da operação de update,
ao final, a operação de update é realizada.
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
Update.
2. Realiza a operação de
Update.
115
Casos de uso reais:
Caso de uso: Checkout
Atores: Usuário
Tipo: Primário e real
Descrição: O usuário fornece o repositório SVN desejado, os
dados de acesso a esse repositório e os artefatos de software a
serem
recuperados,
ao
fim,
os
artefatos
solicitados
são
recuperados.
Seqüência Típica de Eventos
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
checkout clicando em A da Figura
30.
2. O usuário registra o repositório
SVN desejado em A da Janela 1,
o usuário de acesso em B e a
senha em C.
3. O usuário confirma os dados
fornecidos clicando em D da
Janela 1.
4. O usuário registra o artefato
em A da Janela 2 e a versão de
software que deseja receber do
repositório em D e E, ou, em C,
se ele deseja receber a última
versão.
6. O usuário registra o caminho
(PATH) onde serão colocados os
artefatos escolhidos em B.
7. O usuário indica que já
registrou todas as informações
clicando em I.
8.
Realiza
a
operação
solicitada e, ao fim, apresenta
uma mensagem de sucesso,
demonstrada na Figura 23.
116
Seqüências alternativas
Linha 4: O usuário clica em F da Janela 2 e escolhe o artefato
desejado na janela ilustrada na Figura 17. Ao fim, o plug-in
apresenta o artefato escolhido em A da Janela 2.
Linha 6: O usuário clica em G da Janela 2 e escolhe, na janela
ilustrada na Figura 18, o caminho onde deseja salvar os artefatos
solicitados no checkout. Após o usuário escolher p caminho
desejado o plug-in apresenta o caminho escolhido em B da Janela
2.
Linha 8: O usuário pode criar um projeto do Netbeans com os
artefatos que foram adquiridos selecionando essa opção na janela
ilustrada pela Figura 23. Ao selecionar essa opção o Wizard para
criação de novos projetos do Netbeans aparecerá.
Caso de uso: Commit
Atores: Usuário
Tipo: Primário e real
Descrição: O usuário escolhe os artefatos de software que deseja
enviar ao repositório SVN, ao fim, a operação de commit é
realizada.
Seqüência Típica de Eventos
117
Ação do Autor
Resposta do Sistema
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
commit clicando em A da Figura
42.
2. O usuário registra uma
mensagem
descrevendo
a
operação de commit em B da
Figura 42.
3. Mostra os artefatos de
software
disponíveis
para
commit em D da Figura 42.
4. O usuário seleciona os
artefatos que deseja atualizar no
repositório em C da Figura 42.
5.
O
usuário
indica
que
selecionou todos os artefatos
desejados clicando em E da
Figura 42.
6. Realiza a operação.
Caso de uso: Update
Atores: Usuário
Tipo: Primário e real
Descrição: O usuário solicita a realização da operação de update,
ao final, a operação de update é realizada.
Seqüência Típica de Eventos
Ação do Autor
1. Este caso de uso começa
quando um usuário solicita a
realização de uma operação de
Update clicando no item de menu
Checkout All Files do menu SVN.
Resposta do Sistema
2. Realiza a operação de
Update.
118
Diagrama de caso de uso:
Modelos conceituais e diagrama de classes:
119
Diagramas de seqüência:
120
Diagramas de colaboração do caso de uso Checkout:
121
Diagramas de colaboração do caso de uso Commit:
122
Diagrama de colaboração do caso de uso Update:
123
Anexo 3 – Código Fonte
Classe CommitAllAction
package org.svn.subversionmodule.actions;
import java.awt.Dialog;
import org.netbeans.api.project.Project;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CookieAction;
import org.svn.subversionmodule.common.CommitFile;
import org.svn.subversionmodule.domain.CommitControllerImp;
import org.svn.subversionmodule.gui.CommitFrame;
import org.svn.subversionmodule.interfaces.CommitController;
public final class CommitAllAction extends CookieAction {
/* objeto controlador das ações */
private CommitController controller;
/* janela de commit */
private CommitFrame commitFrame;
protected void performAction(Node[] activatedNodes) {
controller = new CommitControllerImp();
commitFrame
=
CommitFrame(controller.getFilesToCommit());
new
/* se o usuário apertou ok temos que realizar o commit */
if(commitFrame.isFinalized()) {
for(CommitFile file : commitFrame.getSelectedFiles()) {
controller.adicionarArtefato(file.getId());
}
controller.adicionarMensagem(commitFrame.getMessage());
controller.terminarOperacao();
}
124
}
/* finalizo td */
commitFrame.clear();
protected int mode() {
return CookieAction.MODE_EXACTLY_ONE;
}
public String getName() {
return
NbBundle.getMessage(CommitAllAction.class,
"CTL_CommitAllAction");
}
protected Class[] cookieClasses() {
return new Class[] {
Project.class
};
}
protected String iconResource() {
return "org/svn/subversionmodule/resources/commit.gif";
}
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
protected boolean asynchronous() {
return false;
}
}
Classe SampleAction
package org.svn.subversionmodule.actions;
import java.awt.Component;
125
import java.awt.Dialog;
import java.awt.event.ActionEvent;
import java.io.File;
import java.text.MessageFormat;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.spi.project.ProjectFactory;
import org.netbeans.spi.project.ui.support.CommonProjectActions;
import org.netbeans.spi.project.ui.support.ProjectChooser;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.WizardDescriptor;
import org.openide.explorer.ExplorerManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.nodes.Node;
import org.openide.util.ContextAwareAction;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.actions.CallableSystemAction;
import org.openide.util.lookup.Lookups;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.svn.subversionmodule.domain.CheckoutControllerImpl;
import org.svn.subversionmodule.gui.*;
import org.svn.subversionmodule.interfaces.CheckoutController;
public final class SampleAction extends CallableSystemAction {
private WizardDescriptor.Panel[] panels;
/* objeto controlador das ações */
private CheckoutController controller;
public void performAction() {
126
controller = new CheckoutControllerImpl();
WizardDescriptor
wizardDescriptor
=
new
WizardDescriptor(getPanels());
//
{0}
will
be
replaced
by
WizardDesriptor.Panel.getComponent().getName()
wizardDescriptor.setTitleFormat(new MessageFormat("{0}"));
wizardDescriptor.setTitle("Checkout");
Dialog
dialog
=
DialogDisplayer.getDefault().createDialog(wizardDescriptor);
dialog.setVisible(true);
dialog.toFront();
File checkoutedPath = new File(((CheckoutWizardWizardPanel2)
panels[1]).clear());
//Bom, aki acabou o checkout wizard, entao vemos se foi finish
boolean
cancelled
=
wizardDescriptor.getValue()
!=
WizardDescriptor.FINISH_OPTION;
if (!cancelled) {
//verifico se o q foi feito update eh um projeto NetBeansValido
FileObject cP = FileUtil.toFileObject(checkoutedPath);
boolean saveSettings = false;
//vou tentar 3x
int tentativas = 0;
while( tentativas < 3) {
if (ProjectManager.getDefault().isProject(cP)) {
saveSettings = this.openProject(cP);
break;
}
tentativas++;
}
}
}
if(tentativas >= 3)
saveSettings = this.createProject(cP);
private boolean openProject(FileObject checkoutedPath) {
127
//pergunto pro cara se ele quer criar um project com os dados do
checkout
Object[] options = {"Open Project", "Close"};
int resp = JOptionPane.showOptionDialog(null,
"<html>The project was checked out.<p>Do you want to
open the project?",
"Checkout Completed",
JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null,
options,
options[0]);
if (resp == JOptionPane.YES_OPTION) {
try {
Project
p
=
ProjectManager.getDefault().findProject(checkoutedPath);
if (p != null) {
this.openProject(p);
}
} catch(Exception e) {
ErrorManager err = ErrorManager.getDefault();
err.annotate(e, "SampleAction::openProject");
err.notify(e);
}
return true;
}
return false;
}
private void openProject(Project p) {
Project[] projects = new Project[] {p};
OpenProjects.getDefault().open(projects, false);
//ja coloco o projeto aberto como main e abro ele
ContextAwareAction
action
=
(ContextAwareAction)
CommonProjectActions.setAsMainProjectAction();
Lookup ctx = Lookups.singleton(p);
Action ctxAction = action.createContextAwareInstance(ctx);
ctxAction.actionPerformed(null);
this.selectAndExpandProject(p);
128
}
private void selectAndExpandProject(final Project p) {
SwingUtilities.invokeLater(new Runnable() {
TopComponent
tc
=
WindowManager.getDefault().findTopComponent("projectTabLogical_t
c");
final
ExplorerManager.Provider
ptLogial
=
(ExplorerManager.Provider) tc;
public void run() {
Node
root
=
ptLogial.getExplorerManager().getRootContext();
// Node projNode = root.getChildren ().findChild(
p.getProjectDirectory().getName () );
Node
projNode
=
root.getChildren().findChild(
ProjectUtils.getInformation( p ).getName() );
if ( projNode != null ) {
try {
ptLogial.getExplorerManager().setSelectedNodes( new
Node[] { projNode } );
} catch (Exception ignore) {}
}
}
});
}
private boolean createProject(FileObject checkoutedPath) {
//pergunto pro cara se ele quer criar um project com os dados do
checkout
Object[] options = {"Create Project...", "Close"};
int resp = JOptionPane.showOptionDialog(null,
"Do you want to create an IDE project from the checked-out
sources?",
"Checkout Completed",
JOptionPane.YES_NO_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null,
options,
129
options[0]);
if (resp == JOptionPane.YES_OPTION) {
this.newProjectWizard(checkoutedPath);
return true;
}
}
return false;
private void newProjectWizard(FileObject checkoutedPath) {
Action action = CommonProjectActions.newProjectAction();
if (action != null) {
action.putValue(CommonProjectActions.EXISTING_SOURCES_FOLD
ER, checkoutedPath);
performAction(action);
}
}
private boolean performAction(Action a) {
if (a == null) {
return false;
}
ActionEvent ae = new ActionEvent(SampleAction.class,
ActionEvent.ACTION_PERFORMED, "command");
try {
a.actionPerformed(ae);
return true;
} catch (Exception e) {
ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
return false;
}
}
private WizardDescriptor.Panel[] getPanels() {
if (panels == null) {
/* Crio os dois paineis de checkout*/
CheckoutWizardWizardPanel1
panel1
CheckoutWizardWizardPanel1(controller);
=
new
130
CheckoutWizardWizardPanel2
CheckoutWizardWizardPanel2(controller);
panel2
=
new
panels = new WizardDescriptor.Panel[] {panel1, panel2};
String[] steps = new String[panels.length];
useful
for (int i = 0; i < panels.length; i++) {
Component c = panels[i].getComponent();
// Default step name to component name of panel. Mainly
// for getting the name of the target chooser to appear in the
// list of steps.
steps[i] = c.getName();
if (c instanceof JComponent) { // assume Swing components
JComponent jc = (JComponent) c;
// Sets step number of a component
jc.putClientProperty("WizardPanel_contentSelectedIndex",
new Integer(i));
// Sets steps names for a panel
jc.putClientProperty("WizardPanel_contentData", steps);
// Turn on subtitle creation on each step
jc.putClientProperty("WizardPanel_autoWizardStyle",
Boolean.TRUE);
// Show steps on the left side with the image on the
background
jc.putClientProperty("WizardPanel_contentDisplayed",
Boolean.TRUE);
// Turn on numbering of all steps
jc.putClientProperty("WizardPanel_contentNumbered",
Boolean.TRUE);
}
}
}
return panels;
}
public String getName() {
return "Checkout...";
}
131
public String iconResource() {
return "org/svn/subversionmodule/resources/checkout.gif";
}
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
protected boolean asynchronous() {
return false;
}
}
Classe UpdateAllAction
package org.svn.subversionmodule.actions;
import org.netbeans.api.project.Project;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CookieAction;
import org.svn.subversionmodule.domain.UpdateControllerImpl;
import org.svn.subversionmodule.interfaces.UpdateController;
public final class UpdateAllAction extends CookieAction {
/* objeto controlador das ações */
private UpdateController controller;
protected void performAction(Node[] activatedNodes) {
controller = new UpdateControllerImpl();
controller.realizarOperacao();
}
protected int mode() {
return CookieAction.MODE_EXACTLY_ONE;
}
132
public String getName() {
return
NbBundle.getMessage(UpdateAllAction.class,
"CTL_UpdateAllAction");
}
protected Class[] cookieClasses() {
return new Class[] {
Project.class
};
}
protected String iconResource() {
return "org/svn/subversionmodule/resources/update.gif";
}
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
protected boolean asynchronous() {
return false;
}
}
Classe CommitFile
/*
* CommitFile.java
*
* Created on 12 de Janeiro de 2007, 19:24
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.common;
/**
133
*
* @author lucio
*/
public class CommitFile {
private int id;
private String fileName;
private String repository;
private String status;
/** Creates a new instance of CommitFile */
public CommitFile() {
}
public CommitFile(int id, String fileName, String repository, String
status) {
this.id = id;
this.fileName = fileName;
this.repository = repository;
this.status = status;
}
public int getId() {
return this.id;
}
public String getFileName() {
return this.fileName;
}
public String getRepository() {
return repository;
}
}
public String getStatus() {
return this.status;
}
134
Classe SVNRepositoryLogin
/*
* SVNRepositoryLogin.java
*
* Created on 8 de Janeiro de 2007, 21:04
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.common;
import java.io.Serializable;
/**
*
* @author lucio
*/
public class SVNRepositoryLogin implements Serializable {
private String url;
private String user;
private String password;
{
/** Creates a new instance of SVNRepositoryLogin */
public SVNRepositoryLogin(String url, String user, String password)
}
this.url = url;
this.user = user;
this.password = password;
public void setUrl(String url) {
this.url = url;
}
public void setUser(String user) {
this.user = user;
}
135
public void setPassword(String Password) {
this.password = password;
}
public String getUrl() {
return this.url;
}
public String getUser() {
return this.user;
}
}
public String getPassword() {
return this.password;
}
Classe CheckoutControllerImpl
/*
* CheckoutControllerImpl.java
*
* Created on 26 de Dezembro de 2006, 16:03
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.domain;
import java.util.Set;
import org.svn.subversionmodule.common.SVNRepositoryLogin;
import org.svn.subversionmodule.interfaces.AuthAccessObject;
import org.svn.subversionmodule.interfaces.CheckoutController;
import
org.svn.subversionmodule.persistence.NetBeansFileAuthAccess;
import org.tmatesoft.svn.core.SVNException;
/**
136
*
* @author lucio
*/
public class CheckoutControllerImpl implements CheckoutController {
private CheckoutOperation operation;
private AuthAccessObject authManager;
/** Creates a new instance of CheckoutControllerImpl */
public CheckoutControllerImpl() {
authManager = new NetBeansFileAuthAccess();
}
/* adiciona o repositóprio e as informações de login a operação
corrente */
public void registrarRepositorio(String svn_url, String user, String
password) throws Exception {
operation = new CheckoutOperation(svn_url, user, password);
authManager.storeAuth(new SVNRepositoryLogin(svn_url, user,
password));
}
/* adiciona um artefato e sua versão a operação corrente */
public void registrarArtefato(String artefato, String versao) {
operation.setDevice(artefato, versao);
}
/* adiciona o o destino da operação corrente */
public void registrarDestino(String destino) {
operation.setPath(destino);
}
/* realiza a operação */
public void terminarOperacao() {
operation.doCheckout();
}
/* retorna os logins que ja foram utilizados para conexão */
public Set<SVNRepositoryLogin> getUsedLogins() {
return authManager.getUsedLogins();
}
137
/* retorna a raiz do repositório */
public String getRepositoryRoot() {
return operation.getRepositoryRoot();
}
}
/* retorna os nós filhos deste nó */
public Set<String> getChildrens(String parent) {
return operation.getChildrens(parent);
}
Classe CheckoutOperation
/*
* CheckoutOperation.java
*
* Created on 26 de Dezembro de 2006, 16:08
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.domain;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNNodeKind;
import
org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.tmatesoft.svn.core.SVNException;
138
import org.tmatesoft.svn.core.SVNURL;
import java.io.File;
/**
*
* @author lucio
*/
public class CheckoutOperation {
private SVNRepository repository;
private SVNRevision revision;
private SVNURL url;
private File destination;
/** Creates a new instance of CheckoutOperation */
public CheckoutOperation(String repository, String user, String
password) throws SVNException {
/* Iniciamos a biblioteca JavaSVN*/
SVNRepositoryFactoryImpl.setup();
this.repository
=
SVNRepositoryFactory.create(SVNURL.parseURIEncoded(repository))
;
this.repository.setAuthenticationManager(SVNWCUtil.createDefaultAut
henticationManager(user, password));
this.repository.testConnection();
}
public void setDevice(String artefato, String revision) {
try {
this.url = SVNURL.parseURIEncoded(artefato);
this.revision = SVNRevision.parse(revision);
} catch(SVNException exp) {
System.out.println("CheckoutOperation: SVNException setting
device: " + exp.getMessage());
}
}
public void setPath(String destination) {
139
}
this.destination = new File(destination);
public void doCheckout() {
try {
ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
SVNUpdateClient
update
=
new
SVNUpdateClient(repository.getAuthenticationManager(), options);
update.doCheckout(url, destination, revision, revision, true);
}catch(SVNException exp) {
System.out.println("CheckoutOperation:
SVNException
checkouting: " + exp.getMessage());
}
}
public String getRepositoryRoot() {
try {
return repository.getRepositoryRoot(true).toString();
} catch(SVNException exp) {
System.out.println("CheckoutOperation: SVNException getting
repository root: " + exp.getMessage());
}
return null;
}
public Set<String> getChildrens(String parent) {
Set<String> childrens = new HashSet<String>();
try {
Collection entries = repository.getDir(parent,
(Collection) null);
for(Object oEntry : entries) {
SVNDirEntry entry = (SVNDirEntry) oEntry;
if (entry.getKind() == SVNNodeKind.DIR) {
childrens.add(entry.getName());
}
}
}catch(Exception e) {
System.out.println("CheckoutOperation:
Exception
childrens: " + e.getMessage());
-1,
null,
getting
140
}
}
}
return childrens;
Classe CommitControllerImp
/*
* CommitControllerImp.java
*
* Created on 12 de Janeiro de 2007, 19:11
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.domain;
import java.util.Set;
import org.svn.subversionmodule.common.CommitFile;
import org.svn.subversionmodule.domain.ProjectCatalog;
import org.svn.subversionmodule.interfaces.CommitController;
/**
*
* @author lucio
*/
public class CommitControllerImp implements CommitController {
/* esse cara me informa os arquivos q podem ser commited */
private ProjectCatalog projectCatalog;
/* o cara que realiza a operação de commit */
private CommitOperation operation;
/** Creates a new instance of CommitControllerImp */
public CommitControllerImp() {
projectCatalog = new ProjectCatalog();
operation = new CommitOperation();
}
141
public void adicionarArtefato(Integer id) {
operation.addDevice(projectCatalog.getDevice(id));
}
public void adicionarMensagem(String message) {
operation.addMessage(message);
}
public void terminarOperacao() {
operation.doCommit();
}
}
public Set<CommitFile> getFilesToCommit() {
return projectCatalog.getFilesToCommit();
}
Classe CommitOperation
/*
* CommitOperation.java
*
* Created on 12 de Janeiro de 2007, 19:28
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.domain;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import org.svn.subversionmodule.common.CommitFile;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.SVNCommitClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
142
/**
*
* @author lucio
*/
public class CommitOperation {
private String message = null;
private Set<CommitFile> files;
/** Creates a new instance of CommitOperation */
public CommitOperation() {
files = new HashSet<CommitFile>();
}
public void addMessage(String message) {
this.message = message;
}
public void addDevice(CommitFile file) {
files.add(file);
}
public void doCommit() {
try {
ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
for(CommitFile file: files) {
File[] path = new File[1];
path[0] = new File(file.getFileName());
SVNCommitClient
commitClient
=
new
SVNCommitClient(file.getRepository().getAuthenticationManager(),
options);
commitClient.doCommit(path, true, message, true, true);
}
} catch(SVNException ex) {
System.out.println("CommitOperation: SVNException comitting:
" + ex.getMessage());
}
}
143
}
Classe ProjectCatalog
/*
* ProjectCatalog.java
*
* Created on 12 de Janeiro de 2007, 19:33
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.domain;
import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ui.OpenProjects;
import org.svn.subversionmodule.common.CommitFile;
import org.tmatesoft.svn.core.SVNException;
import
org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.wc.SVNDirectory;
import org.tmatesoft.svn.core.internal.wc.SVNEntry;
/**
*
* @author lucio
*/
public class ProjectCatalog {
/** Creates a new instance of ProjectCatalog */
public ProjectCatalog() {
/* Iniciamos a biblioteca JavaSVN*/
SVNRepositoryFactoryImpl.setup();
}
144
public CommitFile getDevice(Integer id) {
return null;
}
private Set<CommitFile> getVersionedProjectPaths() {
Set<CommitFile> files = new HashSet<CommitFile>();
/* varro todos os projetos */
Project[] projects = OpenProjects.getDefault().getOpenProjects();
for(int i = 0; i < projects.length; i ++) {
int id = 0;
String fileName = null;
String repository = null;
String status = "versioned";
/* verifico se esse projeto esta em um diretório versionado */
fileName = projects[i].getProjectDirectory().getPath();
File
projectPath
=
new
File(projects[i].getProjectDirectory().getPath());
SVNDirectory file = new SVNDirectory(null, "", projectPath);
if(!file.isVersioned()) continue;
try {
Iterator it = file.getEntries().entries(true);
if(it.hasNext()) {
SVNEntry entry = (SVNEntry) it.next();
repository = entry.getRepositoryRoot();
}
}catch(SVNException exp) {
System.out.println("ProjectCatalog: SVNException
files to commit: " + exp.getMessage());
}
getting
files.add(new CommitFile(id, fileName, repository, status));
}
}
return files;
public Set<CommitFile> getFilesToCommit() {
145
}
}
return this.getVersionedProjectPaths();
public Set<CommitFile> getFilesToUpdate() {
return this.getVersionedProjectPaths();
}
Classe UpdateControllerImpl
/*
* UpdateControllerImpl.java
*
* Created on 18 de Janeiro de 2007, 18:08
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.domain;
import org.svn.subversionmodule.domain.UpdateOperation;
import org.svn.subversionmodule.interfaces.UpdateController;
/**
*
* @author lucio
*/
public class UpdateControllerImpl implements UpdateController {
/* esse cara me informa os arquivos q podem ser commited */
private ProjectCatalog projectCatalog;
/* o cara que realiza a operação de update */
private UpdateOperation operation;
/** Creates a new instance of UpdateControllerImpl */
public UpdateControllerImpl() {
projectCatalog = new ProjectCatalog();
operation = new UpdateOperation();
146
}
}
public void realizarOperacao() {
operation.addDevices(projectCatalog.getFilesToUpdate());
operation.doUpdate();
}
Classe UpdateOperation
/*
* UpdateOperation.java
*
* Created on 18 de Janeiro de 2007, 19:39
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.domain;
import java.io.File;
import java.util.Set;
import org.svn.subversionmodule.common.CommitFile;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
/**
*
* @author lucio
*/
public class UpdateOperation {
Set<CommitFile> files = null;
/** Creates a new instance of UpdateOperation */
public UpdateOperation() {}
147
public void addDevices(Set<CommitFile> files) {
this.files = files;
}
public void doUpdate() {
try {
ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
for(CommitFile file: files) {
SVNUpdateClient
updateClient
=
new
SVNUpdateClient(file.getRepository().getAuthenticationManager(),
options);
updateClient.doUpdate(new
File(file.getFileName()),
SVNRevision.HEAD, true);
}
} catch(SVNException ex) {
System.out.println("CommitOperation: SVNException comitting:
" + ex.getMessage());
}
}
}
Classe CheckOutWizardVisualPanel1
package org.svn.subversionmodule.gui;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.svn.subversionmodule.common.SVNRepositoryLogin;
148
public final class CheckOutWizardVisualPanel1 extends JPanel {
protected CheckoutWizardWizardPanel1 controller;
HashMap<String, SVNRepositoryLogin> logins = new HashMap();
//the components used in connect thread
private JComponent progressComponent;
private JLabel progressLabel;
public CheckOutWizardVisualPanel1(CheckoutWizardWizardPanel1
controller) {
this.controller = controller;
initComponents();
// adiciono ao combo box os logins ja efetuados
for(SVNRepositoryLogin login : controller.getConnectionLogins())
{
logins.put(login.getUrl(), login);
jComboBoxRoot.addItem(login.getUrl());
}
}
// Seto o usuário e o password do login atual
this.setSelectedLogin();
private void setSelectedLogin() {
SVNRepositoryLogin
actualLogin
logins.get(jComboBoxRoot.getSelectedItem());
if(actualLogin == null) return;
jTextFieldUser.setText(actualLogin.getUser());
jTextFieldPassword.setText(actualLogin.getPassword());
controller.fireChangeEvent();
}
=
public String getName() {
return "SVN Root";
}
public String getUrl() {
Object o = jComboBoxRoot.getSelectedItem();
if ((o instanceof String) && o != null)
return (String) o;
149
}
return new String("");
public String getUser() {
return jTextFieldUser.getText();
}
public String getPassword() {
return new String(jTextFieldPassword.getPassword());
}
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">
private void initComponents() {
jLabel2 = new javax.swing.JLabel();
jLabel1 = new javax.swing.JLabel();
jTextFieldUser = new javax.swing.JTextField();
jLabel4 = new javax.swing.JLabel();
jLabel3 = new javax.swing.JLabel();
jLabel5 = new javax.swing.JLabel();
jTextFieldPassword = new javax.swing.JPasswordField();
jComboBoxRoot = new javax.swing.JComboBox();
org.openide.awt.Mnemonics.setLocalizedText(jLabel2, "User:");
org.openide.awt.Mnemonics.setLocalizedText(jLabel1,
Root:");
"SVN
jTextFieldUser.addActionListener(new
java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jTextFieldUserActionPerformed(evt);
}
});
jTextFieldUser.addInputMethodListener(new
java.awt.event.InputMethodListener() {
public
void
caretPositionChanged(java.awt.event.InputMethodEvent evt) {
}
150
public
void
inputMethodTextChanged(java.awt.event.InputMethodEvent evt) {
jTextFieldUserInputMethodTextChanged(evt);
}
});
jTextFieldUser.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyReleased(java.awt.event.KeyEvent evt) {
jTextFieldUserKeyReleased(evt);
}
public void keyTyped(java.awt.event.KeyEvent evt) {
jTextFieldUserKeyTyped(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabel4,
location of SVN Repository defined by SVN root.");
"Specify
org.openide.awt.Mnemonics.setLocalizedText(jLabel3,
"(svn://hostname)");
org.openide.awt.Mnemonics.setLocalizedText(jLabel5,
"Password:");
jTextFieldPassword.addKeyListener(new
java.awt.event.KeyAdapter() {
public void keyReleased(java.awt.event.KeyEvent evt) {
jTextFieldPasswordKeyReleased(evt);
}
});
jComboBoxRoot.setEditable(true);
jComboBoxRoot.addItemListener(new
java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent evt) {
jComboBoxRootItemStateChanged(evt);
}
});
jComboBoxRoot.addInputMethodListener(new
java.awt.event.InputMethodListener() {
151
public
void
caretPositionChanged(java.awt.event.InputMethodEvent evt) {
}
public
void
inputMethodTextChanged(java.awt.event.InputMethodEvent evt) {
jComboBoxRootInputMethodTextChanged(evt);
}
});
jComboBoxRoot.addKeyListener(new
java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
jComboBoxRootKeyPressed(evt);
}
public void keyReleased(java.awt.event.KeyEvent evt) {
jComboBoxRootKeyReleased(evt);
}
public void keyTyped(java.awt.event.KeyEvent evt) {
jComboBoxRootKeyTyped(evt);
}
});
jComboBoxRoot.addMouseListener(new
java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jComboBoxRootMouseClicked(evt);
}
});
org.jdesktop.layout.GroupLayout
org.jdesktop.layout.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout
=
new
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING
)
.add(org.jdesktop.layout.GroupLayout.TRAILING,
layout.createSequentialGroup()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAI
LING)
152
.add(org.jdesktop.layout.GroupLayout.LEADING,
layout.createSequentialGroup()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEA
DING)
.add(jLabel1)
.add(jLabel2)
.add(jLabel5))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEA
DING)
.add(jLabel3)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAI
LING, false)
.add(org.jdesktop.layout.GroupLayout.LEADING,
jTextFieldPassword)
.add(org.jdesktop.layout.GroupLayout.LEADING,
jTextFieldUser,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
145, Short.MAX_VALUE))
.add(jComboBoxRoot,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
379,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED,
Short.MAX_VALUE))
.add(jLabel4,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE))
.add(150, 150, 150))
);
layout.setVerticalGroup(
22,
476,
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING
)
.add(layout.createSequentialGroup()
.add(jLabel4)
.add(14, 14, 14)
153
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jLabel1)
.add(jComboBoxRoot,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jLabel3)
.add(21, 21, 21)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jLabel2)
.add(jTextFieldUser,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jLabel5)
.add(jTextFieldPassword,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addContainerGap(161, Short.MAX_VALUE))
);
}// </editor-fold>
private void jComboBoxRootKeyTyped(java.awt.event.KeyEvent evt)
{
controller.fireChangeEvent();
}
private
void
jComboBoxRootInputMethodTextChanged(java.awt.event.InputMethod
Event evt) {
154
controller.fireChangeEvent();
}
private void jComboBoxRootKeyPressed(java.awt.event.KeyEvent
evt) {
controller.fireChangeEvent();
}
private
void
jComboBoxRootMouseClicked(java.awt.event.MouseEvent evt) {
controller.fireChangeEvent();
}
private
void
jComboBoxRootItemStateChanged(java.awt.event.ItemEvent evt) {
this.setSelectedLogin();
}
private void jComboBoxRootKeyReleased(java.awt.event.KeyEvent
evt) {
controller.fireChangeEvent();
}
private
void
jTextFieldPasswordKeyReleased(java.awt.event.KeyEvent evt) {
controller.fireChangeEvent();
}
private void jTextFieldUserKeyReleased(java.awt.event.KeyEvent
evt) {
controller.fireChangeEvent();
}
private void jTextFieldUserKeyTyped(java.awt.event.KeyEvent evt) {
controller.fireChangeEvent();
}
private
void
jTextFieldUserActionPerformed(java.awt.event.ActionEvent evt) {
}
private
void
jTextFieldUserInputMethodTextChanged(java.awt.event.InputMethodE
vent evt) {
}
void startThreadComponents(JComponent bar) {
155
//desabilito as entradas do usuario
jTextFieldUser.setEditable(false);
jTextFieldPassword.setEditable(false);
jComboBoxRoot.setEditable(false);
//Crio o q sera usado para indicar o progresso da thread
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.stopThread();
}
});
progressComponent = new JPanel();
progressComponent.setLayout(new BorderLayout(6, 0));
progressLabel = new JLabel("Connecting..");
progressComponent.add(progressLabel, BorderLayout.NORTH);
progressComponent.add(bar, BorderLayout.CENTER);
progressComponent.add(stopButton, BorderLayout.LINE_END);
}
this.setLayout(new BorderLayout());
this.add(progressComponent, BorderLayout.SOUTH);
this.revalidate();
this.repaint();
void stopThreadComponents() {
//i remove the visual thread components
this.remove(progressComponent);
//and set the input components
jTextFieldUser.setEditable(true);
jTextFieldPassword.setEditable(true);
jComboBoxRoot.setEditable(true);
}
this.revalidate();
this.repaint();
// Variables declaration - do not modify
156
private javax.swing.JComboBox jComboBoxRoot;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JPasswordField jTextFieldPassword;
public javax.swing.JTextField jTextFieldUser;
// End of variables declaration
}
Classe CheckOutWizardVisualPanel2
package org.svn.subversionmodule.gui;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.DefaultMutableTreeNode;
public final class CheckOutWizardVisualPanel2 extends JPanel {
protected CheckoutWizardWizardPanel2 controller;
/* componentes usados na pela thread de execução de checkout */
private JComponent progressComponent;
private JLabel progressLabel;
157
public CheckOutWizardVisualPanel2(CheckoutWizardWizardPanel2
controller) {
initComponents();
this.controller = controller;
}
public String getName() {
return "Module to Checkout";
}
public String getModule() {
return jTextFieldModule.getText();
}
public String getLocalFolder() {
return jTextFieldLocalFolder.getText();
}
public String getVersion() {
if(jRadioButtonHead.isSelected()) return "HEAD";
return jTextFieldRevision.getText();
}
void startThreadComponents(JComponent bar) {
//desabilito as entradas do usuario
jTextFieldModule.setEditable(false);
jTextFieldLocalFolder.setEditable(false);
jButton1.setEnabled(false);
jButton3.setEnabled(false);
//Crio o q sera usado para indicar o progresso da thread
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.stopThread();
}
});
progressComponent = new JPanel();
158
progressComponent.setLayout(new BorderLayout(6, 0));
progressLabel = new JLabel("Checkouting..");
progressComponent.add(progressLabel, BorderLayout.NORTH);
progressComponent.add(bar, BorderLayout.CENTER);
progressComponent.add(stopButton, BorderLayout.LINE_END);
}
this.setLayout(new BorderLayout());
this.add(progressComponent, BorderLayout.SOUTH);
this.revalidate();
this.repaint();
void stopThreadComponents() {
//i remove the visual thread components
this.remove(progressComponent);
//and set the input components
jTextFieldModule.setEditable(true);
jTextFieldLocalFolder.setEditable(true);
jButton1.setEnabled(true);
jButton3.setEnabled(true);
this.revalidate();
this.repaint();
}
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jLabel3 = new javax.swing.JLabel();
jTextFieldModule = new javax.swing.JTextField();
jTextFieldLocalFolder = new javax.swing.JTextField();
jLabel4 = new javax.swing.JLabel();
jButton1 = new javax.swing.JButton();
jLabel5 = new javax.swing.JLabel();
jButton3 = new javax.swing.JButton();
jLabel6 = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
jRadioButtonHead = new javax.swing.JRadioButton();
jLabel7 = new javax.swing.JLabel();
jLabel8 = new javax.swing.JLabel();
159
jRadioButtonOtherRevision = new javax.swing.JRadioButton();
jTextFieldRevision = new javax.swing.JTextField();
jButton2 = new javax.swing.JButton();
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, "Module:");
org.openide.awt.Mnemonics.setLocalizedText(jLabel3,
Folder: ");
"Local
jTextFieldModule.addKeyListener(new
java.awt.event.KeyAdapter() {
public void keyReleased(java.awt.event.KeyEvent evt) {
jTextFieldModuleKeyReleased(evt);
}
public void keyTyped(java.awt.event.KeyEvent evt) {
jTextFieldModuleKeyTyped(evt);
}
});
jTextFieldLocalFolder.addKeyListener(new
java.awt.event.KeyAdapter() {
public void keyReleased(java.awt.event.KeyEvent evt) {
jTextFieldLocalFolderKeyReleased(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabel4,
"Specify
the SVN module and branch to checkout from SVN repository.");
org.openide.awt.Mnemonics.setLocalizedText(jButton1,
"Browse...");
jButton1.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jButton1MouseClicked(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabel5,
location of local folder to checkout module into.");
"Specify
160
org.openide.awt.Mnemonics.setLocalizedText(jButton3,
"Browse...");
jButton3.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jButton3MouseClicked(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabel6,
SVN working directory)");
"(local
org.openide.awt.Mnemonics.setLocalizedText(jLabel2,
means all modules)");
"(empty
jRadioButtonHead.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(jRadioButtonHead,
"HEAD revision");
jRadioButtonHead.setBorder(javax.swing.BorderFactory.createEmptyB
order(0, 0, 0, 0));
jRadioButtonHead.setMargin(new java.awt.Insets(0, 0, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(jLabel7,
the revision to checkout.");
");
"Specify
org.openide.awt.Mnemonics.setLocalizedText(jLabel8, "Revision:
org.openide.awt.Mnemonics.setLocalizedText(jRadioButtonOtherRevis
ion, "Other revision: ");
jRadioButtonOtherRevision.setBorder(javax.swing.BorderFactory.creat
eEmptyBorder(0, 0, 0, 0));
jRadioButtonOtherRevision.setMargin(new java.awt.Insets(0, 0, 0,
0));
jTextFieldRevision.setEditable(false);
161
org.openide.awt.Mnemonics.setLocalizedText(jButton2,
revisions...");
jButton2.setEnabled(false);
org.jdesktop.layout.GroupLayout
org.jdesktop.layout.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout
=
"Show
new
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING
)
.add(jLabel4)
.add(layout.createSequentialGroup()
.add(jLabel5)
.addContainerGap())
.add(layout.createSequentialGroup()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAI
LING, false)
.add(org.jdesktop.layout.GroupLayout.LEADING,
layout.createSequentialGroup()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEA
DING)
.add(jLabel3)
.add(jLabel8))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEA
DING)
.add(jLabel6)
.add(jRadioButtonHead)
.add(layout.createSequentialGroup()
.add(jRadioButtonOtherRevision)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jTextFieldRevision,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)
210,
162
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jButton2))
.add(jTextFieldLocalFolder,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)))
.add(org.jdesktop.layout.GroupLayout.LEADING,
layout.createSequentialGroup()
.add(jLabel1)
.add(32, 32, 32)
390,
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEA
DING)
.add(jLabel2)
.add(jTextFieldModule,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
430,
Short.MAX_VALUE))))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED,
Short.MAX_VALUE)
6,
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEA
DING)
.add(jButton1)
.add(jButton3))
.add(103, 103, 103))
.add(layout.createSequentialGroup()
.add(jLabel7)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING
)
.add(layout.createSequentialGroup()
.add(jLabel4)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
163
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jLabel1)
.add(jButton1)
.add(jTextFieldModule,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jLabel2)
.add(33, 33, 33)
.add(jLabel5)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jLabel3)
.add(jButton3)
.add(jTextFieldLocalFolder,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEA
DING)
.add(layout.createSequentialGroup()
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jLabel6)
.add(46, 46, 46)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jRadioButtonHead)
.add(jLabel8)))
.add(layout.createSequentialGroup()
.add(49, 49, 49)
164
.add(jLabel7)))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jRadioButtonOtherRevision)
.add(jButton2)
.add(jTextFieldRevision,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.add(87, 87, 87))
);
}// </editor-fold>
private
void
jTextFieldModuleKeyReleased(java.awt.event.KeyEvent evt) {
}
private
jTextFieldModuleKeyTyped(java.awt.event.KeyEvent evt) {
void
}
private
void
jTextFieldLocalFolderKeyReleased(java.awt.event.KeyEvent evt) {
controller.fireChangeEvent();
}
private
void
jButton3MouseClicked(java.awt.event.MouseEvent evt) {
/* Abre uma janela para escolha de diretório
(JFileChooser) */
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Brownse Local Folder");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = chooser.showOpenDialog(this);
if(returnVal == JFileChooser.APPROVE_OPTION) {
jTextFieldLocalFolder.setText(chooser.getSelectedFile().getPath());
}
165
}
{
controller.fireChangeEvent();
private void jButton1MouseClicked(java.awt.event.MouseEvent evt)
/* mostra uma tela com os diretórios do repositório */
jTextFieldModule.setText(ListDialog.showDialog(this,
"Repository
Contents:",
"Browse
SVN
controller.getController()));
}
jLabel1,
Module",
public String clear() {
String temp = jTextFieldLocalFolder.getText();
jTextFieldLocalFolder.setText("");
jTextFieldModule.setText("");
return temp;
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JLabel jLabel7;
private javax.swing.JLabel jLabel8;
private javax.swing.JRadioButton jRadioButtonHead;
private javax.swing.JRadioButton jRadioButtonOtherRevision;
private javax.swing.JTextField jTextFieldLocalFolder;
private javax.swing.JTextField jTextFieldModule;
private javax.swing.JTextField jTextFieldRevision;
// End of variables declaration
}
166
Classe CheckoutWizardWizardPanel1
package org.svn.subversionmodule.gui;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.ErrorManager;
import org.openide.WizardDescriptor;
import org.openide.WizardValidationException;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.svn.subversionmodule.interfaces.CheckoutController;
import org.svn.subversionmodule.common.SVNRepositoryLogin;
public
class
CheckoutWizardWizardPanel1
WizardDescriptor.AsynchronousValidatingPanel {
/* atributos referentes ao wizard */
private
final
Set<ChangeListener>
listeners
HashSet<ChangeListener>(1);
private Component component;
implements
=
new
/* objeto que gerencia as ações */
private CheckoutController controller;
/* objetos para controler da thread de validação da conexão */
private ProgressHandle progress;
private boolean force_validate = false;
private Thread validationThread = null;
private String error = null;
167
{
public CheckoutWizardWizardPanel1(CheckoutController controller)
}
this.controller = controller;
public void setError(String error){
this.error = error;
}
public String getError() {
return this.error;
}
public Set<SVNRepositoryLogin> getConnectionLogins() {
return controller.getUsedLogins();
}
public Component getComponent() {
if (component == null) {
component = new CheckOutWizardVisualPanel1(this);
}
return component;
}
public HelpCtx getHelp() {
return HelpCtx.DEFAULT_HELP;
}
public boolean isValid() {
if (force_validate) return false;
CheckOutWizardVisualPanel1
panel1
(CheckOutWizardVisualPanel1) this.getComponent();
return
(
(panel1.getUrl().length()
!=
(panel1.getUser().length() != 0) &&
(panel1.getPassword().length() != 0) );
}
=
0)
&&
public final void addChangeListener(ChangeListener l) {
synchronized (listeners) {
168
}
}
listeners.add(l);
public final void removeChangeListener(ChangeListener l) {
synchronized (listeners) {
listeners.remove(l);
}
}
protected final void fireChangeEvent() {
Iterator<ChangeListener> it;
synchronized (listeners) {
it = new HashSet<ChangeListener>(listeners).iterator();
}
ChangeEvent ev = new ChangeEvent(this);
while (it.hasNext()) {
it.next().stateChanged(ev);
}
}
public void stopThread() {
try {
if (validationThread == null) return;
validationThread.interrupt();
}catch(SecurityException exp) {
System.out.println(" ===== [ERROR] The current thread cannot
modify this thread");
}
}
public void validate() throws WizardValidationException {
final
CheckOutWizardVisualPanel1
panel1
(CheckOutWizardVisualPanel1) this.getComponent();
validationThread = Thread.currentThread();
this.setError(null);
=
Thread tempThread = new Thread(){
public void run() {
169
try {
controller.registrarRepositorio(panel1.getUrl(),
panel1.getUser(), panel1.getPassword());
}catch(Exception e) {
setError(e.getMessage());
}
}
};
try {
tempThread.start();
tempThread.join();
} catch(InterruptedException e) {
this.setError("Verification stopped by user!");
}
// ao fim, liberamos os botões
force_validate = false;
fireChangeEvent();
panel1.stopThreadComponents();
validationThread = null;
tempThread = null;
}
if(this.getError() != null) {
String temp = this.getError();
this.setError(null);
throw new WizardValidationException(panel1, temp, temp);
}
public void prepareValidation() {
CheckOutWizardVisualPanel1
panel1
(CheckOutWizardVisualPanel1) this.getComponent();
=
progress = ProgressHandleFactory.createHandle("The Thread
handle");
panel1.startThreadComponents(ProgressHandleFactory.createProgres
sComponent(progress));
progress.start();
170
}
}
force_validate = true;
this.fireChangeEvent();
public void readSettings(Object object) {}
public void storeSettings(Object object) {}
Classe CheckoutWizardWizardPanel2
package org.svn.subversionmodule.gui;
import java.awt.Component;
import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.WizardDescriptor;
import org.openide.WizardDescriptor;
import org.openide.WizardDescriptor;
import org.openide.WizardValidationException;
import org.openide.util.HelpCtx;
import org.openide.util.RequestProcessor;
import org.svn.subversionmodule.interfaces.CheckoutController;
public
class
CheckoutWizardWizardPanel2
WizardDescriptor.AsynchronousValidatingPanel {
/* atributos referentes ao wizard */
private
final
Set<ChangeListener>
listeners
HashSet<ChangeListener>(1);
private Component component;
implements
=
new
/* objeto que gerencia as ações */
private CheckoutController controller;
171
/* objetos para controler da thread de validação da conexão */
private Thread validationThread = null;
private ProgressHandle progress;
private boolean force_validate = false;
private String error = null;
{
public CheckoutWizardWizardPanel2(CheckoutController controller)
}
this.controller = controller;
public CheckoutController getController() {
return controller;
}
public Component getComponent() {
if (component == null) {
component = new CheckOutWizardVisualPanel2(this);
}
return component;
}
public HelpCtx getHelp() {
return HelpCtx.DEFAULT_HELP;
}
public boolean isValid() {
// usado na thread de checkout
if (force_validate) return false;
CheckOutWizardVisualPanel2
panel2
(CheckOutWizardVisualPanel2) this.getComponent();
return (panel2.getLocalFolder().length() != 0);
}
=
private String getError() {
return error;
}
public void setError(String error) {
172
}
this.error = error;
public final void addChangeListener(ChangeListener l) {
synchronized (listeners) {
listeners.add(l);
}
}
public final void removeChangeListener(ChangeListener l) {
synchronized (listeners) {
listeners.remove(l);
}
}
protected final void fireChangeEvent() {
Iterator<ChangeListener> it;
synchronized (listeners) {
it = new HashSet<ChangeListener>(listeners).iterator();
}
ChangeEvent ev = new ChangeEvent(this);
while (it.hasNext()) {
it.next().stateChanged(ev);
}
}
public void stopThread() {
try {
if (validationThread == null) return;
validationThread.interrupt();
}catch(SecurityException exp) {
System.out.println("CheckoutWizardWizardPanel2:
SecurityException stopping thread: " + exp.getMessage());
}
}
public void validate() throws WizardValidationException {
final
CheckOutWizardVisualPanel2
panel2
(CheckOutWizardVisualPanel2) getComponent();
validationThread = Thread.currentThread();
this.setError(null);
=
173
Thread connection_thread = new Thread(){
public void run() {
try {
controller.registrarArtefato(panel2.getModule(),
panel2.getVersion());
controller.registrarDestino(panel2.getLocalFolder());
controller.terminarOperacao();
}catch(Exception e) {
setError(e.getMessage());
}
}
};
try {
connection_thread.start();
connection_thread.join();
} catch(Exception e) {
setError("Checkout stopped by user!");
}
force_validate = false;
fireChangeEvent();
panel2.stopThreadComponents();
validationThread = null;
connection_thread = null;
}
if (this.getError() != null) {
String temp = this.getError();
this.setError(null);
throw new WizardValidationException(panel2, temp, temp);
}
public void prepareValidation() {
CheckOutWizardVisualPanel2
panel2
(CheckOutWizardVisualPanel2) this.getComponent();
=
progress = ProgressHandleFactory.createHandle("The Thread
handle");
174
panel2.startThreadComponents(ProgressHandleFactory.createProgres
sComponent(progress));
progress.start();
force_validate = true;
this.fireChangeEvent();
}
public String clear() {
return ((CheckOutWizardVisualPanel2) getComponent()).clear();
}
}
public void readSettings(Object settings) {}
public void storeSettings(Object settings) {}
Classe CommitFrame
/*
* CommitFrame.java
*
* Created on 11 de Janeiro de 2007, 14:47
*/
package org.svn.subversionmodule.gui;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.filechooser.FileSystemView;
import javax.swing.table.DefaultTableModel;
import org.svn.subversionmodule.common.CommitFile;
import org.svn.subversionmodule.gui.CommitTableRender;
175
/**
*
* @author lucio
*/
public class CommitFrame extends javax.swing.JDialog implements
ItemChangeListener {
/* os arquivos habilitados para commit */
Set<CommitFile> filesToCommit;
/* variavel que indica como terminou o dialog */
private boolean finalized = false;
/** Creates new form CommitFrame */
public CommitFrame(Set<CommitFile> filesToCommit) {
this.filesToCommit = filesToCommit;
initComponents();
/* p/ fechar a janela */
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
((CommitFrame) we.getSource()).setFinalized(false);
((CommitFrame) we.getSource()).setVisible(false);
}
});
/* configurando a tabela */
jTable1.setModel(new CommitTableModel());
jTable1.setShowGrid(false);
jTable1.setShowVerticalLines(false);
jTable1.setShowHorizontalLines(false);
jTable1.setDefaultRenderer(CommitTableColumn.class,
CommitTableRender());
jTable1.setDefaultEditor(CommitTableColumn.class,
CommitTableEditor());
new
new
/* populando a tabela com os arquivos */
this.populateTable(filesToCommit);
/* inicio */
176
}
this.setLocationRelativeTo(null);
this.setModal(true);
this.setVisible(true);
this.toFront();
public void setFinalized(boolean finalized) {
this.finalized = finalized;
}
public boolean isFinalized() {
return this.finalized;
}
public Set<CommitFile> getSelectedFiles() {
CommitTableModel
model
=
jTable1.getModel();
return model.getSelectedFiles();
}
is
(CommitTableModel)
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">
private void initComponents() {
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jPanel1 = new javax.swing.JPanel();
jScrollPane2 = new javax.swing.JScrollPane();
jTable1 = new javax.swing.JTable();
jCheckBox1 = new javax.swing.JCheckBox();
jCheckBox2 = new javax.swing.JCheckBox();
jLabel1 = new javax.swing.JLabel();
jPanel2 = new javax.swing.JPanel();
jScrollPane1 = new javax.swing.JScrollPane();
jTextArea1 = new javax.swing.JTextArea();
177
setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHIN
G_ON_CLOSE);
setTitle("Commit - All Projects");
setModal(true);
jButton1.setText("Commit");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText("Cancel");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Files
to Commit:"));
jScrollPane2.setBackground(new java.awt.Color(255, 255, 255));
jTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
){
},
new String [] {
"File", "Status", "Repository Path"
}
boolean[] canEdit = new boolean [] {
false, false, false
};
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit [columnIndex];
}
});
jScrollPane2.setViewportView(jTable1);
178
jCheckBox1.setSelected(true);
jCheckBox1.setText("Show unversioneds files");
jCheckBox1.setBorder(javax.swing.BorderFactory.createEmptyBorder(
0, 0, 0, 0));
jCheckBox1.setMargin(new java.awt.Insets(0, 0, 0, 0));
jCheckBox1.addActionListener(new
java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jCheckBox1ActionPerformed(evt);
}
});
jCheckBox2.setText("Select / deselect all");
jCheckBox2.setBorder(javax.swing.BorderFactory.createEmptyBorder(
0, 0, 0, 0));
jCheckBox2.setMargin(new java.awt.Insets(0, 0, 0, 0));
jCheckBox2.addActionListener(new
java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jCheckBox2ActionPerformed(evt);
}
});
jLabel1.setText("0 files selected, 0 files total");
org.jdesktop.layout.GroupLayout
jPanel1Layout
org.jdesktop.layout.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
=
new
jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.L
EADING)
.add(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLay
out.LEADING)
179
.add(jScrollPane2,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)
.add(jCheckBox2)
.add(jPanel1Layout.createSequentialGroup()
.add(jCheckBox1)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED,
Short.MAX_VALUE)
.add(jLabel1)))
.addContainerGap())
);
jPanel1Layout.setVerticalGroup(
520,
256,
jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.L
EADING)
.add(org.jdesktop.layout.GroupLayout.TRAILING,
jPanel1Layout.createSequentialGroup()
.add(jScrollPane2,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
184,
Short.MAX_VALUE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLay
out.BASELINE)
.add(jCheckBox1)
.add(jLabel1))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(jCheckBox2))
);
jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Mes
sage:"));
jTextArea1.setColumns(20);
jTextArea1.setRows(4);
jScrollPane1.setViewportView(jTextArea1);
180
org.jdesktop.layout.GroupLayout
jPanel2Layout
org.jdesktop.layout.GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(
=
new
jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.L
EADING)
.add(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.add(jScrollPane1,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
520,
Short.MAX_VALUE)
.addContainerGap())
);
jPanel2Layout.setVerticalGroup(
jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.L
EADING)
.add(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.add(jScrollPane1,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
80,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE))
);
org.jdesktop.layout.GroupLayout
layout
org.jdesktop.layout.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
=
new
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING
)
.add(org.jdesktop.layout.GroupLayout.TRAILING,
layout.createSequentialGroup()
.addContainerGap()
181
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAI
LING)
.add(org.jdesktop.layout.GroupLayout.LEADING, jPanel1,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)
.add(org.jdesktop.layout.GroupLayout.LEADING, jPanel2,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)
.add(layout.createSequentialGroup()
.add(jButton1)
.add(16, 16, 16)
.add(jButton2)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING
)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(jPanel2,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(20, 20, 20)
.add(jPanel1,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE,
org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(17, 17, 17)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BAS
ELINE)
.add(jButton2)
.add(jButton1))
182
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE))
);
pack();
}// </editor-fold>
private void jButton1ActionPerformed(java.awt.event.ActionEvent
evt) {
this.finalized = true;
this.setVisible(false);
}
private
void
jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {
CommitTableModel
model
=
(CommitTableModel)
jTable1.getModel();
model.setSelectedToAll(jCheckBox2.isSelected());
this.updateSelectedFilesStatus(model.getSelectedFilesNumber(),
model.getRowCount());
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent
evt) {
this.finalized = false;
this.setVisible(false);
}
private void populateTable(Set<CommitFile> filesToCommit) {
/* uso um JChosser para pegar os ícones dos arquivos, tem jeito
mais facil??? */
JFileChooser tempChooser = new JFileChooser();
FileSystemView
systemView
=
tempChooser.getFileSystemView();
if(systemView == null) {
System.out.println("CommitFrame:
error
getting
FileSystemView");
return;
}
183
CommitTableModel
jTable1.getModel();
model
=
(CommitTableModel)
for(CommitFile file : filesToCommit) {
File tempFile = new File(file.getFileName());
Icon fileIcon = systemView.getSystemIcon(tempFile);
model.addRow(new
Object[]
{new
CommitTableColumn(file.getFileName(), fileIcon, this), file.getStatus(),
file.getRepository()});
}
this.updateSelectedFilesStatus(model.getSelectedFilesNumber(),
model.getRowCount());
}
private
void
jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {
CommitTableModel
model
=
(CommitTableModel)
jTable1.getModel();
model.clear();
if(jCheckBox1.isSelected()) {
this.populateTable(filesToCommit);
return;
}
Set<CommitFile> versionedFiles = new HashSet<CommitFile>();
for(CommitFile file : filesToCommit) {
if(file.getStatus().equalsIgnoreCase(new
String("nonversioned"))) continue;
versionedFiles.add(file);
}
this.populateTable(versionedFiles);
}
public String getMessage() {
return jTextArea1.getText();
}
public void clear() {
184
CommitTableModel
model
=
(CommitTableModel)
jTable1.getModel();
model.clear();
jTextArea1.setText(null);
jCheckBox1.setSelected(true);
jCheckBox2.setSelected(false);
jLabel1.setText("0 files selected, 0 files total");
}
private void updateSelectedFilesStatus(int selectedFiles,
totalFiles) {
jLabel1.setText(String.valueOf(selectedFiles) +
" files selected, " +
String.valueOf(totalFiles) +
" files total");
}
int
public void itemStateChanged(boolean isSelected) {
CommitTableModel
model
=
(CommitTableModel)
jTable1.getModel();
this.updateSelectedFilesStatus(model.getSelectedFilesNumber(),
model.getRowCount());
}
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JCheckBox jCheckBox1;
private javax.swing.JCheckBox jCheckBox2;
private javax.swing.JLabel jLabel1;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JTable jTable1;
private javax.swing.JTextArea jTextArea1;
// End of variables declaration
185
Classes
CommitTableRender,
CommitTableModel e CommitTableColumn
CommitTableEditor,
/*
* CommitTableRender.java
*
* Created on 15 de Janeiro de 2007, 22:13
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.EventObject;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractCellEditor;
import javax.swing.DefaultCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.event.CellEditorListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.DefaultCellEditor;
import javax.swing.Icon;
import org.jdesktop.layout.GroupLayout;
import org.svn.subversionmodule.common.CommitFile;
186
/**
*
* @author lucio
*/
public class CommitTableRender implements TableCellRenderer {
/** Creates a new instance of CommitTableRender */
public CommitTableRender() {}
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row, int
column) {
if(column != 0) return null;
JPanel panel = ((CommitTableColumn) value).getPanel();
if (isSelected)
panel.setBackground(SystemColor.textHighlight);
else
panel.setBackground(SystemColor.window);
}
}
panel.repaint();
return panel;
class CommitTableEditor extends
TableCellEditor {
public CommitTableEditor() {}
AbstractCellEditor
implements
public Object getCellEditorValue() {
return null;
}
public Component getTableCellEditorComponent(JTable
Object value, boolean isSelected, int row, int column) {
if(column != 0) return null;
}
table,
JPanel panel = ((CommitTableColumn) value).getPanel();
return panel;
187
public boolean isCellEditable(EventObject eventObject) {
return true;
}
public boolean shouldSelectCell(EventObject eventObject) {
return true;
}
public boolean stopCellEditing() {
return true;
}
public void cancelCellEditing() {
}
public
void
cellEditorListener) {
}
public
void
cellEditorListener) {
}
}
addCellEditorListener(CellEditorListener
removeCellEditorListener(CellEditorListener
class CommitTableModel extends AbstractTableModel {
private String[] columnNames = {"File", "Status", "Repository Path"};
private Object[][] data = new Object[100][3];
//private Map<Integer, Object[]> data = new HashMap<Integer,
Object[]>();
int rows = 0;
public void clear() {
this.data = new Object[100][3];
rows = 0;
fireTableDataChanged();
}
public void setSelectedToAll(boolean selected) {
for(Integer i = 0; i < rows; i++) {
188
((CommitTableColumn)
data[i][0]).getCheckBox().setSelected(selected);
}
fireTableDataChanged();
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return rows;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
/*
* JTable uses this method to determine the default renderer/
* editor for each cell. If we didn'
t implement this method,
* then the last column would contain text ("true"/"false"),
* rather than a check box.
*/
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) {
//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
if (col == 0) {
return true;
} else {
return false;
}
189
}
public void setValueAt(Object value, int row, int col) {
if(row >= rows) return;
data[row][col] = value;
fireTableCellUpdated(row, col);
}
public void addRow(Object[] row) {
data[rows] = row;
fireTableCellUpdated(rows, 0);
fireTableCellUpdated(rows, 1);
fireTableCellUpdated(rows, 2);
rows++;
}
public Set<CommitFile> getSelectedFiles() {
Set<CommitFile> selectedFiles = new HashSet<CommitFile>();
for(Integer i = 0; i < rows; i++) {
CommitTableColumn firstColumn = (CommitTableColumn)
data[i][0];
if(!firstColumn.getCheckBox().isSelected()) continue;
selectedFiles.add(new
CommitFile(i,
firstColumn.getLabel().getText(),
(String) data[i][2], (String) data[i][1]));
}
return selectedFiles;
}
public int getSelectedFilesNumber() {
int selectedFiles = 0;
for(Integer i = 0; i < rows; i++) {
if(!((CommitTableColumn)
data[i][0]).getCheckBox().isSelected()) continue;
selectedFiles++;
}
return selectedFiles;
}
}
190
class CommitTableColumn implements ItemListener {
private JPanel panel = null;
private JCheckBox checkBox = null;
private JLabel label = null;
private ItemChangeListener observer;
public CommitTableColumn(String
ItemChangeListener observer) {
this.observer = observer;
fileName,
Icon
fileIcon,
/* criamos o JCheckBOX*/
checkBox = new JCheckBox();
checkBox.addItemListener(this);
checkBox.setPreferredSize(new Dimension(5,5));
checkBox.setBackground(Color.white);
checkBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0,
0, 0, 0));
checkBox.setMargin(new java.awt.Insets(0, 0, 0, 0));
/* criamos o label */
JLabel label = new JLabel(fileName, fileIcon, JLabel.CENTER);
/* criamos o panel */
panel = new JPanel();
panel.setBackground(Color.white);
GroupLayout jPanel3Layout = new GroupLayout(panel);
panel.setLayout(jPanel3Layout);
jPanel3Layout.setHorizontalGroup(
jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.L
EADING)
.add(jPanel3Layout.createSequentialGroup()
.add(checkBox)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(label)
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE))
191
);
jPanel3Layout.setVerticalGroup(
jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.L
EADING)
.add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLay
out.BASELINE)
.add(checkBox)
.add(label))
);
}
public JPanel getPanel() {
return this.panel;
}
public JCheckBox getCheckBox() {
return this.checkBox;
}
public JLabel getLabel() {
return this.label;
}
public void itemStateChanged(ItemEvent itemEvent) {
observer.itemStateChanged(itemEvent.getStateChange()
itemEvent.SELECTED);
}
}
==
interface ItemChangeListener {
public void itemStateChanged(boolean isSelected);
}
Classe ListDialog
package org.svn.subversionmodule.gui;
192
import java.util.Iterator;
import java.util.Set;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.svn.subversionmodule.interfaces.CheckoutController;
public class ListDialog extends JDialog implements ActionListener {
private static ListDialog dialog;
private static String value = "";
private JTree repository_tree;
private DefaultTreeModel treeModel;
private CheckoutController controller;
/* Um cara estático que apresenta um ListDialog */
public static String showDialog(Component frameComp, Component
locationComp, String labelText, String title, CheckoutController
controller) {
Frame
frame
=
JOptionPane.getFrameForComponent(frameComp);
dialog = new ListDialog(frame,
locationComp,
labelText,
title, controller);
dialog.setVisible(true);
return value;
}
private void setValue(String newValue) {
value = newValue;
repository_tree.setSelectionPath(new TreePath(value));
}
193
private ListDialog(Frame frame, Component locationComp, String
labelText, String title, CheckoutController controller) {
super(frame, title, true);
this.controller = controller;
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(this);
final JButton setButton = new JButton("OK");
setButton.setActionCommand("Set");
setButton.addActionListener(this);
setButton.setEnabled(false);
getRootPane().setDefaultButton(setButton);
try {
//Iniciando a JTree
DefaultMutableTreeNode
top
=
DefaultMutableTreeNode(controller.getRepositoryRoot());
treeModel = new DefaultTreeModel(top);
repository_tree = new JTree(treeModel);
new
repository_tree.getSelectionModel().setSelectionMode(TreeSelectionM
odel.SINGLE_TREE_SELECTION);
//Set the icon for leaf nodes.
DefaultTreeCellRenderer
renderer
DefaultTreeCellRenderer();
renderer.setLeafIcon(renderer.getOpenIcon());
repository_tree.setCellRenderer(renderer);
=
new
addDirectories(top, "");
repository_tree.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
try {
setButton.setEnabled(true);
TreePath
path
=
(TreePath)
repository_tree.getSelectionPath();
194
DefaultMutableTreeNode
parent_node
=
(DefaultMutableTreeNode) path.getLastPathComponent();
path = new TreePath(addDirectories(parent_node,
pathToString(path)).getPath());
repository_tree.collapsePath(path);
repository_tree.repaint();
} catch(Exception exc) {}
} else {
if (repository_tree.getSelectionPath() != null)
setButton.setEnabled(true);
}
}
});
JScrollPane treeScroller = new JScrollPane(repository_tree);
treeScroller.setPreferredSize(new Dimension(250, 400));
treeScroller.setAlignmentX(LEFT_ALIGNMENT);
JPanel listPane = new JPanel();
listPane.setLayout(new
BoxLayout(listPane,
BoxLayout.PAGE_AXIS));
JLabel label = new JLabel(labelText);
label.setLabelFor(repository_tree);
listPane.add(label);
listPane.add(Box.createRigidArea(new Dimension(0,5)));
listPane.add(treeScroller);
listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
//Lay out the buttons from left to right.
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new
BoxLayout(buttonPane,
BoxLayout.LINE_AXIS));
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10,
10, 10));
buttonPane.add(Box.createHorizontalGlue());
buttonPane.add(cancelButton);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
buttonPane.add(setButton);
195
//Put everything together, using the content pane'
s
BorderLayout.
Container contentPane = getContentPane();
contentPane.add(listPane, BorderLayout.CENTER);
contentPane.add(buttonPane, BorderLayout.PAGE_END);
//Initialize values.
pack();
setLocationRelativeTo(locationComp);
} catch (Exception svne) {
System.err.println("error
while
listing
svne.getMessage());
//ListDialog.dialog.setVisible(false);
}
}
entries:
/*private Map<String, Boolean> getDirectories(String path) {
try {
//return svnManager.getRepository().getDir(path, -1,
(Collection) null);
return
}catch(Exception e) {
System.out.println("ListDialog::getDirectories:
"
e.getMessage());
}
}*/
"
+
null,
+
return null;
private
DefaultMutableTreeNode
addDirectories(DefaultMutableTreeNode top, String path) {
Set<String> childrens = controller.getChildrens(path);
DefaultMutableTreeNode node = null;
}
for(String children : childrens) {
top.add(new DefaultMutableTreeNode(children));
}
return node;
196
private String pathToString(TreePath path) {
String temp = new String("");
Object[] o = path.getPath();
for (int i = 0; i < o.length; i++) {
if (o[i] instanceof DefaultMutableTreeNode) {
Object p = ( (DefaultMutableTreeNode) o[i]).getUserObject();
if (p instanceof String)
temp += (String) p + "/";
}
}
return temp;
}
}
//Handle clicks on the Ok and Cancel buttons.
public void actionPerformed(ActionEvent e) {
if ("Set".equals(e.getActionCommand())) {
TreePath path = (TreePath) repository_tree.getSelectionPath();
String temp = pathToString(path);
ListDialog.value = temp;
}
ListDialog.dialog.setVisible(false);
}
Interface AuthAccessObject
/*
* AuthAccessObject.java
*
* Created on 11 de Janeiro de 2007, 09:01
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.interfaces;
import java.util.Set;
197
import org.svn.subversionmodule.common.SVNRepositoryLogin;
/**
*
* @author lucio
*/
public interface AuthAccessObject {
/* persiste essa authenticação */
public void storeAuth(SVNRepositoryLogin login);
}
/* retorna todas as autenticações */
public Set<SVNRepositoryLogin> getUsedLogins();
Interface CheckoutController
/*
* CheckoutController.java
*
* Created on 26 de Dezembro de 2006, 15:57
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.interfaces;
import java.util.Set;
import org.svn.subversionmodule.common.SVNRepositoryLogin;
/**
*
* @author lucio
*/
public interface CheckoutController {
/* adiciona o repositóprio e as informações de login a operação
corrente */
public void registrarRepositorio(String svn_url, String user, String
passsword) throws Exception;
198
/* adiciona um artefato e sua versão a operação corrente */
public void registrarArtefato(String artefato, String versao);
/* adiciona o o destino da operação corrente */
public void registrarDestino(String destino);
/* realiza a operação */
public void terminarOperacao();
/* retorna os logins que ja foram utilizados para conexão */
public Set<SVNRepositoryLogin> getUsedLogins();
/* retorna a raiz do repositório */
public String getRepositoryRoot();
}
/* retorna os nós filhos deste nó */
public Set<String> getChildrens(String parent);
Interface CommitController
/*
* CommitController.java
*
* Created on 11 de Janeiro de 2007, 19:47
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.interfaces;
import java.util.Set;
import org.svn.subversionmodule.common.CommitFile;
/**
*
* @author lucio
199
*/
public interface CommitController {
/* retorna os arquivos que estão disponpiveis para commit */
public Set<CommitFile> getFilesToCommit();
/* Adiciona um artefato para ser commited */
public void adicionarArtefato(Integer id);
/* Adiciona uma mensagem que descreve esse commit */
public void adicionarMensagem(String message);
}
/* realiza o commit dos artefatos adicionados */
public void terminarOperacao();
Interface UpdateController
/*
* UpdateController.java
*
* Created on 18 de Janeiro de 2007, 18:07
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.interfaces;
/**
*
* @author lucio
*/
public interface UpdateController {
/* realiza a operação de Update*/
public void realizarOperacao();
}
200
Classe NetBeansFileAuthAccess
/*
* NetBeansFileAuthAccess.java
*
* Created on 11 de Janeiro de 2007, 09:04
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.svn.subversionmodule.persistence;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.Set;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.svn.subversionmodule.common.SVNRepositoryLogin;
import org.openide.filesystems.Repository;
import org.svn.subversionmodule.interfaces.AuthAccessObject;
/**
*
* @author lucio
*/
public class NetBeansFileAuthAccess implements AuthAccessObject {
private
Set<SVNRepositoryLogin>
logins
=
new
HashSet<SVNRepositoryLogin>();
private FileObject folder = null;
private FileObject file = null;
private FileLock lock = null;
/** Creates a new instance of NetBeansFileAuthAccess */
public NetBeansFileAuthAccess() {
try {
//se n tem folderObject, crio ele
201
if
(
(folder
=
Repository.getDefault().getDefaultFileSystem().getRoot().getFileObject
("Settings")) == null)
folder
=
Repository.getDefault().getDefaultFileSystem().getRoot().createFolder(
"Settings");
//se n tem settingFile, crio ele
if ((file = folder.getFileObject("AuthSettings","Cfg")) == null)
file = folder.createData("AuthSettings","Cfg");
//pego as informações do arquivo
this.retrieveLogins();
} catch (IOException ex) {
System.out.println("NetBeansFileAuthAccess:
creating manager: " + ex.getMessage());
}
}
private void dumpLogins() {
try {
//salvo o map no arquivo
lock = file.lock();
ObjectOutputStream
objectOutStr
ObjectOutputStream(file.getOutputStream(lock));
objectOutStr.writeObject(logins);
objectOutStr.close();
lock.releaseLock();
} catch (IOException ex) {
System.out.println("NetBeansFileAuthAccess:
dumping logins: " + ex.getMessage());
}
}
IO
Exception
=
new
IO
Exception
private void retrieveLogins() {
try {
ObjectInputStream
objectInStr
=
new
ObjectInputStream(file.getInputStream());
logins = (Set<SVNRepositoryLogin>) objectInStr.readObject();
202
objectInStr.close();
} catch (IOException ex) {
System.out.println("NetBeansFileAuthAccess:
retrieving logins: " + ex.getMessage());
} catch (Exception ex) {
System.out.println("NetBeansFileAuthAccess:
retrieving logins: " + ex.getMessage());
}
}
IO
Exception
Exception
public void storeAuth(SVNRepositoryLogin login) {
logins.add(login);
this.dumpLogins();
}
}
public Set<SVNRepositoryLogin> getUsedLogins() {
return logins;
}
Arquivo layer.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem
1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Actions">
<folder name="SVN">
<file
name="org-svn-subversionmodule-actionsCommitAllAction.instance">
<attr
name="instanceClass"
stringvalue="org.svn.subversionmodule.actions.CommitAllAction"/>
</file>
<file
name="org-svn-subversionmodule-actionsSampleAction.instance">
<attr
name="instanceClass"
stringvalue="org.svn.subversionmodule.actions.SampleAction"/>
</file>
203
<file
name="org-svn-subversionmodule-actionsUpdateAllAction.instance">
<attr
name="instanceClass"
stringvalue="org.svn.subversionmodule.actions.UpdateAllAction"/>
</file>
</folder>
</folder>
<folder name="Menu">
<folder name="SVN">
<file
name="org-svn-subversionmodule-actionsCommitAllAction.shadow">
<attr name="originalFile" stringvalue="Actions/SVN/org-svnsubversionmodule-actions-CommitAllAction.instance"/>
</file>
<file
name="org-svn-subversionmodule-actionsSampleAction.shadow">
<attr name="originalFile" stringvalue="Actions/SVN/org-svnsubversionmodule-actions-SampleAction.instance"/>
</file>
<attr
name="org-svn-subversionmodule-actionsSampleAction.shadow/org-svn-subversionmodule-actionsSampleAction.shadow" boolvalue="true"/>
<file
name="org-svn-subversionmodule-actionsUpdateAllAction.shadow">
<attr name="originalFile" stringvalue="Actions/SVN/org-svnsubversionmodule-actions-UpdateAllAction.instance"/>
</file>
<file
name="org-svn-subversionmodule-actionsseparatorAfter.instance">
<attr
name="instanceClass"
stringvalue="javax.swing.JSeparator"/>
</file>
<attr
name="org-svn-subversionmodule-actionsCommitAllAction.shadow/org-svn-subversionmodule-actionsseparatorAfter.instance" boolvalue="true"/>
<attr
name="org-svn-subversionmodule-actionsseparatorAfter.instance/org-svn-subversionmodule-actionsSampleAction.shadow" boolvalue="true"/>
204
<attr
name="org-svn-subversionmodule-actionsUpdateAllAction.shadow/org-svn-subversionmodule-actionsSampleAction.shadow" boolvalue="true"/>
</folder>
</folder>
</filesystem>
205
Download