Monday, April 26, 2010
Boa Fonte para Aprender .NET
de conteúdo gratuito que ensina sobre como iniciar na tecnologia .NET e ASP.NET .
http://www.devmedia.com.br/websys.3/webreader.asp?cat=59&revista=easynetmag_1#a-2519
Sunday, April 25, 2010
Processo de Desenvolvimento baseado em Software Livre para um Simulador de Robôs
Humberto Cardoso Marchezi - Departamento de Engenharia Elétrica, UFES, 2004
Resumo:
1. Introdução
2. Uma Visão Geral dos Processos Utilizados em Software Livres
2.1 O Processo de Desenvolvimento de Software do Projeto Mozilla
- A base de código do Mozilla é uma das maiores e rápidas dentro dos projetos de sofware livre. O seu tamanho é comparável ao kernel do Linux mas é maior que todo o ambiente gráfico do Xfree86. O projeto do browser é estimado em dois milhões de linhas de código.
- O número de desenvolvedores é alto, muito deles são pagos diretamente pela Netscape, OEone, Sun, IBM e outras.
- Uma infraestrutura foi desenvolvida para lidar com requisitos de organização massiva
- O projeto Mozilla tem como objetivo criar aplicações fáceis de usar para usuários finais ao contrário da maioria dos projetos de software livre que consideram o usuário também como um especialista no domínio
2.1.1 Modularidade e a “Posse“ do Módulo:
2.1.2 Desenvolvimento Dirigido a “Bugs”
- Proprietário: A pessoa encarregada do “bug”
- Sumário: Uma descrição on-line do “bug”
- Attachments: Arquivos atachados ao “bug” com casos de teste, captura de tela ou modificações na base-de-código
- Comentários: Comentários descrevendo e discutindo o problema
- Estado: O estado corrente do “bug” que pode ser: NÃO-CONFIRMADO, NOVO, DESIGNADO (a um desenvolvedor), RESOLVIDO, REABERTO, VERIFICADO, FECHADO
- Severidade e Prioridade: Descreve o impacto do “bug” e em que grau de importância este deve estar para ser consertado
2.1.3 Requisitos
2.1.4 Design
2.1.5 Desenvolvimento Distribuído e Revisões Formais
- Um desenvolvedor trabalhando num “bug” produz uma correção (“patch”) que é um arquivo texto gerado que descreve as diferenças nas linhas de código desse desenvolvedor e a última versão do código no repositório
- A correção é atachada no “bug” através sistema Bugzilla e enviada pelo desenvolvedor requisitando uma revisão
- O revisor que pode ser um proprietário do módulo ou alguém familiar com o código, aprova a revisão ou pede novas mudanças
2.1.6 Garantia da Qualidade e a Triagem de Bugs
2.2 Ferramentas Utilizadas no Projeto Mozilla
- Desenvolvedores usam diariamente para enviar correções e requisitar uma revisão de código.
- O grupo que trata da garantia da qualidade usa essa ferramenta para medir o progresso e para relatar ou fazer a triagem dos “bugs”
- Gerentes usam a ferramenta para alocar desenvolvedores e monitorar o progresso nas questões mais importantes do desenvolvimento
- Permite consultas para verificar as últimas alterações (check-ins) no repositório
- Mostra os comentários das alterações (check-ins) com hyperlink para o “bug” na ferramenta Bugzilla.
- Interface para visualizar diferenças entre versões de arquivos no repositório.
- Identificação visual de quais desenvolvedores são responsáveis por quais seções de código
- Provê um mecanismo de grafo que mostra diferentes versões de arquivos no repositório do projeto.
- vermelho indica que a compilação falhou
- laranja indica compilação bem-sucedida mas caso-de-teste falhou
- verde indica compilação e testes bem-sucedidos
- amarelo indica compilação e/ou testes ainda em progresso
3. Uma Proposta de Processo de Software Livre para um Simulador de Robôs
3.1 Caso-de-Estudo: Simulação e Programação de Robôs
- Leitura dos Comandos – verifica erros a nível de sintaxe na programação
- Verificação da Viabilidade dos Comandos – verifica se os comandos são fisicamente viáveis de serem realizados pelo robô dadas as restrições físicas das juntas e do ambiente onde este está inserido
- Execução dos Comandos – executa os comandos passados por meio de uma animação
3.2 Modelo do Processo Proposto
- Início do projeto
- Captura de novos requisitos
- Documentação da análise e design
- Ambiente de desenvolvimento proposto descrevendo um conjunto de ferramentas
3.2.1 Como inicia o projeto ?
- Padronização do código incluindo nomenclatura de classes, métodos, variáveis e comentários conforme o exemplo a seguir:
- Classes com nome de substantivo iniciadas por letra maiúscula. Ex: Robot
- Variáveis com letra minúscula e prefixo que identifique o tipo. Ex: srtName
- Padronização da documentação o que pode ser feito através de templates que os membros devem preencher para a documentação de casos-de-uso e componentes.
- Projeto: Robotbuilder
- Pacote: Componente de Tarefa
- Nome: Parser
- Descrição: descrição do objetivo do componente
- Conteúdo: lista de métodos com uma descrição
- Exemplo: exemplo de uso
3.2.2 Captura de Novos Requisitos
3.2.3 Documentação da Análise e Design
- Análise de Requisitos: diagramas de casos-de-uso, especificação dos casos-de-uso
- Análise: diagramas de classes, digramas de sequência e diagramas de estado
- Projeto: diagramas de classes e de seqüências detalhados
- Implementação: Comentários padronizados nos métodos
- Testes: Casos de testes documentados
3.3 Qual o ambiente de desenvolvimento proposto ?
4. Conclusões
- classificação de projetos de software livre.
- criação de uma metodologia baseada na automação da análise.
- uso do conhecimento adquirido para produzir modelos nos permitam entender o desenvolvimento de software livre facilitando na tomada de decisões baseadas na experiência adquirida.
Referências
Comparação Doc-View e Model-View-Contoller
Humberto Cardoso Marchezi – Data 25/05/2005
TÓPICOS ESPECIAIS EM PROGRAMAÇÃO
Introdução
O objetivo desse artigo é explicar os dois padrões de projeto mais utilizados para arquitetura de sistemas, que são o MVC e o Doc-View, e comparar as vantagens e desvantagens no uso de cada um deles.Além disso, um documento contém também as funcionalidades principais da aplicação encapsuladas através dos métodos executados através das mensagens vindas das visões ou da interface gráfica.
Por fim, as informações de um documento podem ser representadas por várias visões, por exemplo, os dados de velocidade de um veículo no tempo no formato tabular ou gráfico, aumentando, dessa forma, a modularidade do programa.
O Framework MVC
Framework Doc-View versus Framework MVC
Ao desenvolver uma aplicação para dar suporte ao um tipo de aplicação cliente, misturar regras de negócio e acesso a dados com lógica específica de interface gráfica para apresentação e controle não é uma abordagem adequada. Como diferentes aplicações precisam ser desenvolvidas para cada tipo de cliente, um código que não pertence a interface é replicado em cada aplicação o que resulta em esforço extra para na implementação, no teste e na manutenção.utilizar qualquer um desses frameworks. Permite a reutilização do código que diz respeito a regra de negócio.
Entretanto o que na prática tem se observado é que a classe Controlador no padrão MVC cresce na mesma proporção que a classe Visão no padrão Doc-View a medida que aumenta a complexidade no controle da interface gráfica e na interação com o usuário.
Conclusões
A partir das comparações feitas pode-se concluir é mais interessante analisar como a aplicação a ser desenvolvida vai funcionar antes de escolher um dos dois padrões apresentados.Thursday, April 1, 2010
Validação de Entidades usando Microsoft Validation Application Block
http://msdn.microsoft.com/en-us/library/cc511802.aspx
Dessa forma, ao invés de validar os objetos dentro das propriedades ou métodos, esses campos podem ser validados
através de Decorators.( [ ] )
Exemplo: ( tirado de http://msdn.microsoft.com/en-us/library/cc511619.aspx )
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
public class Customer
{
private string firstName;
private string lastName;
private DateTime dateOfBirth;
private string email;
private Address address;
[StringLengthValidator(1, 50, Ruleset="RuleSetA",
MessageTemplate="First Name must be between 1 and 50 characters")]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[StringLengthValidator(1, 50, Ruleset = "RuleSetA",
MessageTemplate = "Last Name must be between 1 and 50 characters")]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
[RelativeDateTimeValidator(-120, DateTimeUnit.Year, -18,
DateTimeUnit.Year, Ruleset="RuleSetA",
MessageTemplate="Must be 18 years or older.")]
public DateTime DateOfBirth
{
get { return dateOfBirth; }
set { dateOfBirth = value; }
}
[RegexValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*",
Ruleset = "RuleSetA")]
public string Email
{
get { return email; }
set { email = value; }
}
[ObjectValidator("ValidAddress", Ruleset="RuleSetA")]
public Address Address
{
get { return address; }
set { address = value; }
}
[RangeValidator(0, RangeBoundaryType.Inclusive, 1000000,
RangeBoundaryType.Inclusive, Ruleset="RuleSetA",
MessageTemplate="Rewards points cannot exceed 1,000,000")
public int RewardPoints
{
get { return rewardPoints; }
set { rewardPoints = value; }
}
}
Depois o Customer pode ser validado de uma vez só da seguinte forma:
Exemplo: ( http://msdn.microsoft.com/en-us/library/cc511605.aspx )
Customer cust = new Customer(); ValidationResults results = Validation.Validate(cust, customerRuleSetCombo.Text);
Os objetos também podem implementar um método que faz todas as validações de uma só vez.
Exemplo: ( http://msdn.microsoft.com/en-us/library/cc511640.aspx )
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
[HasSelfValidation]
public class TemperatureRange
{
private int min;
private int max;
// ...
[SelfValidation]
public void CheckTemperature(ValidationResults results)
{
if (max < min)
results.AddResult(new ValidationResult("Max less than min", this, "", "", null));
}
}
É importante notar que o Validation Application Block pode ser usado em aplicações ASP.NET, Winforms e WCF. ( http://msdn.microsoft.com/en-us/library/cc511714.aspx )
* Integrando Validations no Winforms: http://msdn.microsoft.com/en-us/library/cc511884.aspx
* Integrando Validations em ASP.NET: http://msdn.microsoft.com/en-us/library/cc511745.aspx
* Integranto Validations no WCF: http://msdn.microsoft.com/en-us/library/cc511692.aspx
Thursday, January 21, 2010
Modelo-Visão-Controlador
- Visão - Representa a interface gráfica da funcionalidade. Não tem código é apenas uma máscara.
- Controlador - Faz a ponte entre a interface gráfica e a lógica de negócio
- Modelo - São as classes usadas para descrever a lógica de negócio ( classes do problema ( DP ) e classes de persistência (DAO) ) ou ainda podem ser classes da camada de serviço
- ler/escrever dados nos componentes
- habilitar/desabilitar componentes
- mostrar/esconder componentes
- outras operações que forem necessárias
O tráfego de dados entre o Formulário ou WebForm e o controlador é feito via Data Transfer Objects (DTO) o que evita que objetos de negócio sejam levados diretamente para a interface e causar problemas ja que a sessão desses objetos já estaria fechada.
ICadastroClienteView
public interface ICadastroClienteView
{
long IdClient { get; set; }
string NomeCliente { get; set; }
int CodDocumentoCliente { get; }
IList Documentos { set; }
event EventHandler NovoCliente;
event EventHandler InclusaoCliente;
event EventHandler ConsultaCliente;
}
JanCadastroCliente (Winforms)
public class JanCadastroCliente : System.Windows.Forms.Form, ICadastroClienteView
{
: : : : : // código do formulário
// Formulário deve possuir uma referência para o seu controlador correspondente
private ControladorCadastroCliente controlador = new ControladorCadastroCliente(this);
// Le ou mostra o Id do Cliente num label
public long IdCliente
{
get { return Int64.Parse(this.lbIdCliente.Text); }
set { this.lbIdCliente.Text = Int64.Parse(this.lbIdCliente.Text); }
}
// Le ou mostra o nome do cliente num textbox
public string NomeCliente // Le/escreve dados no textBox
{
get { return this.txtNomeCliente.Text; }
set { this.txtNomeCliente.Text = value; }
}
// Le ou mostra os documentos de um cliente
public IListDocumentos
{
set { this.cmbbxDocumentos.DataSource = value; }
get { return this.cmbbxDocumentos.DataSource; }
}
// Le um id de cliente num componente numeric para fins de pesquisa
public long IdClientePesquisa
{
get { return this.numIdClientePesquisa.Value; }
}
public JanCadastroCliente()
{
}
public void JanCadastroCliente_btNovoCliente(Object sender,EventArgs e)
{
if (this.NovoCliente != null) { this.NovoCliente(sender,e); }
}
public void JanCadastroCliente_btInclusaoCliente(Object sender,EventArgs e)
{
if (this.InclusaoCliente != null) { this.InclusaoCliente(sender,e); }
}
public void JanCadastroCliente_btConsultaCliente(Object sender,EventArgs e)
{
if (this.ConsultaCliente != null) { this.ConsultaCliente(sender,e); }
}
}
WebCadastroCliente (Webforms)
public class WebCadastroCliente: System.Windows.Forms.Form, ICadastroClienteView
{
: : : : : // código do formulário
// Formulário deve possuir uma referência para o seu controlador correspondente
private ControladorCadastroCliente controlador;
protected void Page_Load(object sender, System.EventArgs e)
{
controlador = new ControladorCadastroCliente(this);
}
// Le ou mostra o Id do Cliente num label
public long IdCliente
{
get { return Int64.Parse(this.lbIdCliente.Text); }
set { this.lbIdCliente.Text = Int64.Parse(this.lbIdCliente.Text); }
}
// Le ou mostra o nome do cliente num textbox
public string NomeCliente // Le/escreve dados no textBox
{
get { return this.txtNomeCliente.Text; }
set { this.txtNomeCliente.Text = value; }
}
// Le ou mostra os documentos de um cliente
public IListDocumentos
{
set { this.cmbbxDocumentos.DataSource = value; }
get { return this.cmbbxDocumentos.DataSource; }
}
// Le um id de cliente num componente numeric para fins de pesquisa
public long IdClientePesquisa
{
get { return this.numIdClientePesquisa.Value; }
}
public void WebCadastroCliente_btNovoCliente(Object sender,EventArgs e)
{
if (this.NovoCliente != null) { this.NovoCliente(sender,e); }
}
public void WebCadastroCliente_btInclusaoCliente(Object sender,EventArgs e)
{
if (this.InclusaoCliente != null) { this.InclusaoCliente(sender,e); }
}
public void WebCadastroCliente_btConsultaCliente(Object sender,EventArgs e)
{
if (this.ConsultaCliente != null) { this.ConsultaCliente(sender,e); }
}
}
ControladorCadastroCliente
public class ControladorCadastroCliente
{
private ICadastroClienteView _view;
private ServicoCadastroCliente _servicoCadastroCliente;
public ControladorCadastroCliente(ICadastroClienteView view)
{
this._view = view;
this._view.NovoCliente += new EventHandler(LimparCliente);
this._view.InclusaoCliente += new EventHandler(IncluirCliente);
this._view.ConsultaCliente += new EventHandler(ConsultarCliente);
this._servicoCadastroCliente = new ServicoCadastroCliente();
}
public void LimparCliente(Object sender, EventArgs e)
{
this.IdCliente = 0;
this.NomeCliente = string.Empty;
this.Documentos = new List();
}
public void IncluirCliente(Object sender, EventArgs e)
{
long idCliente = this._servicoCadastroCliente.Incluir(
this._view.NomeCliente,
this._view.Documentos);
this._view.IdCliente = idCliente;
}
public void ConsultarCliente(Object sender, EventArgs e)
{
ClienteDTO dto = this._servicoCadastroCliente( this.IdClientePesquisa );
this._view.IdCliente = dto.Id;
this._view.NomeCliente = dto.Nome;
this._view.Documentos = dto.Documentos;
}
}
DocumentoDTO
public class DocumentoDTO
{
public int CodDocumento { get; set; }
public string Descricao { get; set; }
// É a forma como o DTO vai aparecer no combobox ou listbox, etc.
// Mostra apenas a descricao
public override string ToString()
{
return descricao;
}
}
Saturday, December 19, 2009
Desenvolvimento Ágil
Como resposta a isso, surgiu o movimento ágil que tenta descobrir melhores maneiras de desenvolver
um software e de ajudar a outras pessoas a fazer o mesmo.
O desenvolvimento ágil segue o conjunto de '''valores''' abaixo:
- Indíviduos e interações mais importantes que processos e ferramentas
- Software funcionando mais importante que documentação compreensiva
- Colaboração com o cliente mais importante que negociação contratual
- Responder às mudanças mais importante que seguir um caminho a risca
A partir desses valores, '''12 princípios''' foram indentificados:
- Garantir a satisfação do consumidor entregando rapidamente e continuamente softwares funcionais
- Softwares funcionais são entregues frequentemente (semanas, ao invés de meses)
- Softwares funcionais são a principal medida de progresso do projeto
- Até mesmo mudanças tardias de escopo no projeto são bem-vindas
- Cooperação constante entre pessoas que entendem do 'negócio' e desenvolvedores
- Projetos surgem através de indivíduos motivados, e que deve existir uma relação de confiança
- Design do software deve prezar pela excelência técnica
- Simplicidade
- Rápida adaptação às mudanças
- Indivíduos e interações mais importantes do que processos e ferramentas
- Software funcional mais do que documentação extensa
- Colaboração com clientes mais importantes do que negociação de contratos
- Responder a mudanças mais importantes do que seguir um plano
Baseadas nos princípios e valores do movimento ágil surgiram metodologias:
- XP - Extreme Programming (Programação Extrema)
- Scrum
- FDD - Feature Driven Development (Desenvolvimento Orientado a Recurso)
- Etc ....
Programação extrema (do inglês eXtreme Programming), ou simplesmente XP, é uma metodologia ágil para equipes pequenas e médias e que irão desenvolver software com requisitos vagos e em constante mudança. Para isso, adota a estratégia de constante acompanhamento e realização de vários pequenos ajustes durante o desenvolvimento de software.
Valores
- Comunicação
- Simplicidade
- Feedback
- Coragem
- Respeito
- Feedback rápido
- Presumir simplicidade
- Mudanças incrementais
- Abraçar mudanças
- Trabalho de qualidade
Para aplicar os valores e princípios durante o desenvolvimento de software, XP propõe uma série de práticas. Há uma confiança muito grande na sinergia entre elas, os pontos fracos de cada uma são superados pelos pontos fortes de outras.
Planejamento
- Estórias são escritas
- Projeto divido em interações (Cada interação produz uma versão FUNCIONAL do sistema)
- Planejamento da interação inicia cada interação
- Planejamento da versão cria um cronograma (Normalmente cada interação = 2 a 3 semanas)
- Fazer releases pequenas e frequentes (Assim o cliente pode detectar erros antecipadamente)
- Dar ao time um ambiente de trabalho aberto e dedicado
- Atribuir um ritmo sustentável (SEM HORAS EXTRAS!)
- Um standup meeting (reunião em pé) inicia cada dia de trabalho
- A velocidade do projeto é medida
- Trocar pessoas de lugar (significa revezar uma parte do sistema - todos os programares devem passar por todas as partes do sistema)
- Consertar (modificar) o próprio XP para adequar a realidade do projeto / empresa
- Simplicidade
- Escolha uma metáfora para representar o sistema
- Utilizar cartões CRC para sessões de desenho (Ajuda no desenho de classes - CRC = Classe Reponsabilidade e Colaboração)
- Criar soluções de ponta para descobrir respostas para as difíceis problemas técnicos ou de desenho.
- NENHUMA funcionalidade é adicionar antecipamente (Nada de "Vai que o cliente pode sprecisar um dia ...")
- Refatore o código quando e onde for possível (Refatorar = organizar o código-fonte)
- Cliente deve estar sempre disponível (em local próximo de preferência)
- Códigos devem seguir padrões (Assim todos os programadores podem compreendê-los)
- Codificar o testes unitário primeiro (O teste guiará a implementação da classe)
- Toda a programação de produção deve ser feita em par (Um deve ser o instrutor e o outro o aprendiz)
- Uma integração deve ocorrer por vez para cada par de programadores
- Integrar o código sempre
- Configurar um computador dedicado para integração
- Utilizar posse coletiva (Qualquer programador pode contribuir com novas idéias para alterar ou adicionar novas funcionalidades ao código. Assim não existirão gargalos)
- Todo o código deve ter testes unitários
- Todo o código deve passar por testes unitários antes de passar para a produção
- Quando um bug (erro) é encontrado, um teste unitário deve ser criado para simular essa situação
- Testes de aceitação devem ser rodados com freqüência e a pontuação publicada
Thursday, July 16, 2009
Noções de Caso de Uso
Casos-de-uso são uma maneira popular de expressar requisitos de software. Eles são populares porque eles são práticos. O caso-de-uso preenche o espaço entre as necessidades do usuário e a funcionalidade do sistema através da descrição da intenção do usuárioe da resposta do sistema para cada interação entre os dois.
Um ator especifica um papel que pode ser assumido por uma pessoa, parte de um hardware ou um componente de software. Cada ator tem uma certa responsabilidade operacional imposta pelos processos e as regras de negócio dentro do domínio. Para dar conta de suas responsabilidades, um ator deve desempenhar um certo número de operações. Para isso, um ator quer essas operações seja facilitda por uma aplicação de software. A partir daí são definidos os correspondentes objetivos a serem preechidos pelo sistema. Esses objetivos nos levam às funcionalidades do sistema expressos através de casos-de-uso onde cada caso-de-uso é responsável por atingir um objetivo.
Na figura acima, por exemplo, o ator Balconista, no caso uma pessoa, tem como objetivo o pagamento de contas de Pacientes.
Para um objetivo ser alcançado, alguma ação deve tomada para alcança-lo. Para um caso-de-uso, isso é feito através de uma interação com o sistema. A descrição da interação de um caso-de-uso se divide um duas partes: um curso básico que descreve uma sequência principal de interação onde tudo da certo e um curso alternativo onde qualquer alternativa e interrupção do curso báscio de interação como partes opcionais e alternativas, recuperação de erro do negócio ou tratamento de falha.
2. Exemplo Simples de Caso-de-Uso
Caso-de-Uso: Tirar dinheiro de um caixa eletrônico
Curso Normal: ( Curso Básico )
1. O usuário introduz o cartão no caixa eletrônico
2. O caixa eletrônico propõe varias operações
3. O usuário aperta o botão ``saque''
4. O usuário escolhe a conta (ex.: ``conta corrente'')
5. O usuário entra a valor do saque
6. O usuário entra sua senha
7. O caixa eletrônico verifica a senha com o banco e o saldo da conta caixa eletrônico da o dinheiro para o usuário
8. O caixa eletrônico imprime um recibo
3. Padrão de Caso-de-Uso
Por questões de organização uma estrutura de documento deve ser usada para a descrição de casos-de-uso. Abaixo podemos ver um template de caso-de-uso utilizado pela PMV.
___________________________Descrição de Caso de Uso
Projeto: SIAR
Sub-Sistema:
Pacote: XXXXXX
Sub-Pacote: XXXXX
Nome do Caso de Uso: XXXXXXX
Analista: XXXXX
Data de Criação: dd/mm/aaaa
Versão: 1.0
Data de Última Alteração: dd/mm/aaaa
Descrição: XXXXXXXX
Função/Método: XXXXX.XXXXXXXX(XXX,XXXX,XXX)
Pre-Condição: XXXXXXXXXXXX
Curso Normal:
Cenário 1 (c1)
XXXXXX.
XXXXXX.
Se XXXXXX, XXXXXXX ( a1, a2 )
XXXXXX
Se XXXXXX, retorna a linha 3
Cenário 2 (c2)
XXXXXX
XXXXXX
Se XXXXX, XXXXXXX ( a2 )
Para cada XXXXXXXX, fazer até XXXXXXXXX
XXXXXXXX
XXXXXXXX
XXXXX
XXXXX
Curso Alternativo:
Cenário Alternativo 1 (a1)
XXXXXX.
XXXXXX
Cenário Alternativo 2 (a2)
XXXXXX.
XXXXXX
Restrições de Integridade:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

