Esfinge Metadata


Visão geral

O EsfingeMETADATA é um meta-framework extensível para leitura e validação de metadados, que visa a simplificar e auxiliar principalmente o desenvolvimento de frameworks baseados em metadados.Além disso, também pode ser utilizado em aplicações baseadas em anotações customizadas.

Dependencias do framework

Como regra geral, é necessária a adição dos arquivos JAR do Esfinge Metadata com as seguintes dependências: commons-beanutils-1.9.2.jar commons-collections-3.2.2.jar commons-logging-1.2.jar

Maven

O Esfinge Metadata usa como padrão o Maven para gerenciar as dependências. Por isso você deve colocar o artefato do Esfinge Metadata, o maven se encarrega de baixar as dependências que serão usadas.


<dependency>
    <groupId>net.sf.esfinge</groupId>
    <artifactId>metadata</artifactId>
    <version>1.5.6</version>
</dependency>                          

Localização de Metadados

O Esfinge Metadata possui uma funcionalidade que recupera e localiza anotações. De acordo com configurações feitas nas anotações que estão sendo procuradas, é possível que sejam buscadas em outros lugares relacionados ao elemento, como, por exemplo, dentro de outras anotações que ele possui.

Para localizar os metadado use o método AnnotationFinder.findAnnotation() , este vai retornar uma lista de Anotações.

Exemplo de Localização de Metadatos:

Em um primeiro momento criamos a anotação chamada @AnnotationInElement .

@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInElement {
}

Em seguida criamos uma classe que usa a anotação criada anteriormente, neste exemplo chamaremos de ElementWithMetadata .

@AnnotationInElement
public class ElementWithMetadata {
}

E depois criamos a busca por essa anotação no codigo usando o metodo estático AnnotationFinder.findAnnotation() que recebe como parametros um elemento com as Anotações neste caso a classe ElementWithMetadata e a anotação alvo que existe o elemento, neste caso a annotação @AnnotationInElement e tem como retorno uma lista de anotações, em caso falso ele vai retornar uma lista com zero posições.

@Test
public void metadataLocatorTestWithAnnotation(){
List<Annotation> annotationList = AnnotationFinder.findAnnotation(ElementWithMetadata.class, AnnotationInElement.class); ; assertTrue(annotationList.size()>0); assertTrue(annotationList.get(0) instanceof AnnotationInElement);
}

Anotações para Localização de Metadados

@SearchInsideAnnotations

Esta anotação configura quando uma anotação pode ser definida dentro de outra. Quando uma anotação com essa configuração é pesquisada, a implementação da API deve procurá-la dentro de cada uma das anotações do elemento de destino;

Exemplo:

Como exemplo em primeiro lugar, criamos uma anotação contendo @SearchInsideAnnotations que diz que a anotação está dentro de outra anotação. Neste caso a anotação @AnnotationInAnnotation está anotada com a @SearchInsideAnnotations

@Retention(RetentionPolicy.RUNTIME)
@SearchInsideAnnotations
public @interface AnotationInAnotation {
}                          

Em seguida, criamos uma anotação que contem a anotação criada anteriormente, e tambem colocamos a anotação @SearchInsideAnnotations .

@Retention(RetentionPolicy.RUNTIME)
@AnotationInAnotation
@SearchInsideAnnotations
public @interface AnotationInElement {
}

Criamos uma classe que contem a anotação criada anteriormente.

@AnotationInElement
public class ElementWithMetadata {
}

Por ultimo criamos a busca pela anotação @AnnotationInAnnotation na classe ElementWithMetadata este vai verificar se o @AnnotationInAnnotation existe na classe existe no elemento anotado ou em anotações do elemento anotado.

@Test
public void metadataLocatorTestWithAnnotation() {
   List<Annotation> annotationList = AnnotationFinder.findAnnotation(ElementWithMetadata.class, AnotationInAnotation.class);
   assertTrue(annotationList.size()>0);
   assertTrue(annotationList.get(0) instanceof AnotationInAnotation);
}

@SearchOnEnclosingElements

Esta anotação configura quando uma anotação pode ser definida no escopo do seu elemento de inclusão. Quando uma anotação com essa configuração é pesquisada em um método, se não for encontrada, a implementação da API deve verificar se ela está presente em sua classe;

Exemplo:

Criemos uma anotação para anotar a classe, contendo a anotação @SearchOnEnclosingElements, que é a anotação responsavel por dizer em que nivel a anotação está na classe.

@Retention(RetentionPolicy.RUNTIME)
@SearchOnEnclosingElements
public @interface AnnotationInClass {

}

Em seguida criamos uma classe annotada com a anotação criada anteriormente, com um atributo ou metodo, neste exemplo foi criado com um atributo.

@AnnotationInClass
public class ClassWithAnnotation {
   public Object selectedElement;
}

Em seguida executamos o AnnotationFinder.findAnnotation() este caso de teste, vai retornar uma lista de anotações contendo a anotação @AnnotationInClass esta anotação vai retorar um elemento de nivel acima do elemento que está sendo buscado.

@Test
public void testWithAnnotation() throws NoSuchFieldException, SecurityException {
   List<Annotation> annotationList = 
AnnotationFinder.findAnnotation(ClassWithAnnotation.class.getDeclaredField("selectedElement"),
AnnotationInClass.class); assertTrue(annotationList.size()>0); assertTrue(annotationList.get(0) instanceof AnnotationInClass); }
@SearchOnAbstractions
Esta anotação configura quando uma anotação pode ser definida em abstrações, como superclasses e interfaces. Quando uma anotação com essa configuração é pesquisada em tipos, a implementação da API deve verificar se suas superclasses e interfaces. Quando é pesquisado em um método, ele deve procurar em um método que substitui a partir da superclasse e em um método que implementa em interfaces.

Exemplo:

Criemos uma anotação para anotar a classe, contendo a anotação @SearchOnAbstractions ela nos diz que é para procurar em niveis de abstração.
@SearchOnAbstractions
public @interface AnnotedInterface {

}
Uma interface anotada com a anotação criada anteriormente.
@AnnotedInterface
public interface InterfaceWithAnnotation {
}
Uma classe que implementa a interface que contem a anotação
public class ValidClass implements InterfaceWithAnnotation {
}
Em seguida executamos o AnnotationFinder.findAnnotation() este caso de teste, vai retornar uma lista de anotações contendo a anotação @AnnotedInterface esta anotação vai retorar um elemento anotado na abstração e pode ser utilizado com os outros locators.
public class SearchOnAbstractionsTest {
   @Test
   public void test() {
      List<Annotation> annotationList = AnnotationFinder.findAnnotation(ValidClass.class, AnnotedInterface.class);
      assertTrue(annotationList.size()>0);
      assertTrue(annotationList.get(0) instanceof AnnotedInterface);
   }
}


Metadata Reader

Apesar das anotações poderem ser recuperadas diretamente de um elemento, normalmente as informações contidas nelas são todas colocadas em um objeto que armazena essas informações para a aplicação poder acessar futuramente. Essa classe é chamada de Metadata Container e isso é um padrão de projeto. Essa funcionalidade do Esfinge Metadata faz o mapeamento de anotações para uma classe que armazena essas informações, poupando o trabalho de leitura dos metadados.

Uso de Anotações Basicas

Nesta seção falaremos, sobre as anotações basicas do EsfingeMetadata, e exemplificamos o seu uso, e de como se deve fazer uma implementação para pegar os dados de mapeamento de classes.

Em primeiro momento criamos um Metadata Container, este deve ser anotato com uma anotação, @ContainerFor(), esta anotação recebe como parametro, um enun denominado ContainerTarget, este pode ser TYPE, FIELD, METHOD, ALL, logo em criamos os atributos anotados, @ElementName, recebe o nome do elemento alvo, @ContainsAnnotation, recebe como parametro uma annotação e verifica se na classe existe a anotação passada, @ReflectionReference, pega a classe de referencia da anotação, @AnnotationProperty, verifica se a anotação existe, e pega o valor do campo passado pelo elemento property.

@ContainerFor(ContainerTarget.TYPE)
public class ContainerClass {
@ElementName private String elementName;
@ContainsAnnotation(AnnotationInClass.class) private boolean existAnnotation; @ReflectionReference private Class<?> reference; @AnnotationProperty(annotation = AnnotationInClass.class, property ="elementInAnnotation") private String elementInAnnotation; public String getElementInAnnotation() { return elementInAnnotation; } public void setElementInAnnotation(String elementInAnnotation) { this.elementInAnnotation = elementInAnnotation; }
public String getElementName() { return elementName; } public void setElementName(String elementName) { this.elementName = elementName; } public boolean isExistAnnotation() { return existAnnotation; } public void setExistAnnotation(boolean existAnnotation) { this.existAnnotation = existAnnotation; } public Class<?> getReference() { return reference; } public void setReference(Class<?> reference) { this.reference = reference; } }

Para esse exemplo criamos uma anotação neste caso denominada de @AnnotationInClass essa anotação vai ser usada futuramente para verificar se a anotação existe e se tem a propriedade elementInAnnotation()   esta anotação vai ser usada neste caso para o @ContainsAnnotation e para o @AnnotationProperty.

@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInClass{				 {
	String elementInAnnotation();
}
Criaremos tambem a classe que vai ser utilizada pelo AnnotationReader, ela possui as informações da classe @AnnotationInClass, que neste caso ela passa por parametro uma string com um valor.

@AnnotationInClass(elementInAnnotation="valueOfAnnotation")
public class ClassForReader {
}

Criaremos o caso de teste, rodando o AnnotationReader, e executamos o readingAnnotationsTo(), passando o elemento anotado e o Metadata Container, que neste caso é a classe ClassForReader, e o ContainerClass, tambem vemos os resultados do que deve ser retornado em cada um dos testes neste caso.


@Test
public void testAnnotationsBasic() throws Exception {
   ContainerClass container;
   AnnotationReader annotationReader = new AnnotationReader();

   container = annotationReader.readingAnnotationsTo(ClassForReader.class, ContainerClass.class);
   assertTrue(container.isExistAnnotation());
   assertEquals(ClassForReader.class.getName(), container.getElementName());
   assertEquals(ClassForReader.class, container.getReference());
   assertEquals("valueOfAnnotation", container.getElementInAnnotation());
}

Listas de anotações basicas

Anotação Elemento Alvo Uso
@ContainsAnnotation Classe,metodo, field. Esta anotação de Fields verifica se existe a anotação selecionada no elemento alvo. Ele contem apenas o método value() que retorana se a anotação selecionada existe, esta anotação deve ser usada em tipos primitivos boolean ou classes do tipo Boolean.
@AnnotationProperty Metodo,field. Esta anotação verifica se existe uma anotação anotada na classe alvo, e alem disso verifica, se esta existe uma propriedade com um nome descrito.
@ElementName Classe,metodo, field. Esta anotação de field pega o nome do elemento que foi anotado e passa para o field do container.
@ReflectionReference Classe,metodo, field. Esta anotação de field pega a classe do elemento que foi anotado e passa para o field que foi anotado com esse elemento.

Anotações Avançadas

São anotações usadas para leitura e execução em elementos avançado perante o Metadata Container, que são usados para leitura e processamento de elementos como elementos e metodos.

Em um primeiro momento criaremos um container de classe, armazena as informações de classe, atributos e metodos, foi criado os metodos getAnnotedMethod() e getAnnotedField() para facilitar as criação de caso de teste. Neste caso temos as anotações @ProcessMethods que pega as informações dos metodos e armazena em um container de metodos, e o @AllMethodWith que armazena as anotações anotadas em um container de metodos, enquanto o @ProcessFields armazena todos os elementos de atributos em um container, e o @AllFieldsWith armazena os atributos com determinada anotação em um container de Atributos, todos eles podem ser utilizado como Map, Set e List.

@ContainerFor(ContainerTarget.TYPE)
public class ContainerClass {

   @ElementName
   private String elementName;

   @ReflectionReference
   private Class<?>  reference;

   @ProcessMethods
   private List<MethodContainer> methodContainer;

   @ProcessFields
   private List<FieldContainer> fieldContainer;

   @AllMethodsWith(AnnotationInElement.class)
   private List<MethodContainer> methodWithAnnotation;

   @AllFieldsWith(AnnotationInElement.class)
   private List<FieldContainer> fieldWithAnnotation;

  //METODOS GETS E SETS OMITIDOS public MethodContainer getAnnotedMethod(String name) { for(MethodContainer container : methodContainer){ if(container.getElementName().equals(name)){ return container; } } return null;
} public FieldContainer getAnnotedField(String name){ for(FieldContainer container : fieldContainer){ if(container.getElementName().equals(name)){ return container; } } return null; } }

Aqui foi criado o container de atributos, ele é responsavel por armazenar as informações de atributos passado por parametro, para gravar as informações no container de atributos, neste caso o ContainerTarget tem que ser de FIELDS.

@ContainerFor(ContainerTarget.FIELDS)
public class FieldContainer {
   @ElementName
   private String elementName;

   @ContainsAnnotation(AnnotationInElement.class)
   private boolean existAnnotation;

  //METODOS GETS E SETS OMITIDOS
}

Aqui foi criado o container de metodos, ele é responsavel por armazenar as informações de atributos passado por parametro, para gravar as informações no container de atributos, neste caso o ContainerTarget tem que ser de METHODS.

@ContainerFor(ContainerTarget.METHODS)
public class MethodContainer {

	@ElementName
	private String elementName;

	@ContainsAnnotation(AnnotationInElement.class)
	private boolean existAnnotation;
	
//METODOS GETS E SETS OMITIDOS }
Aqui criaremos uma anotação que tem como alvo os metodos e atributos, pois são eles que estão sendo analizados nesse momento.
@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface AnnotationInElement {
}

Foi criadao aqui tambem a classe que vai ser lida pelo AnnotationReader, e com os seus metodos e atributos anotados.

package net.sf.esfinge.metadata.examples.annotationReader.advanced;

@AnnotationInClass
public class ClassForReader {
  private String fieldWinouthAnnotation; @AnnotationInElement private String fieldWithAnnotation; @AnnotationInElement public void methodWithAnnotation(){ }
public void methodWinouthAnnotation(){ } }

Caso de teste com as classes de leitura de metodos e atributos com os seus respectivos casos de teste rodando corretamente.

public class AnnotationReaderTest {
   @Test
   public void testAnnotationsBasic() throws Exception {
	ContainerClass container;
	AnnotationReader annotationReader = new AnnotationReader();

	container = annotationReader.readingAnnotationsTo(ClassForReader.class, ContainerClass.class);

	assertEquals(ClassForReader.class, container.getReference());

	MethodContainer methodWithAnnotation = container.getAnnotedMethod("methodWithAnnotation");
	assertEquals("methodWithAnnotation", methodWithAnnotation.getElementName());
	assertTrue(methodWithAnnotation.isExistAnnotation());
			MethodContainer methodWinouthAnnotation = container.getAnnotedMethod("methodWinouthAnnotation");
	assertEquals("methodWinouthAnnotation", methodWinouthAnnotation.getElementName());
	assertFalse(methodWinouthAnnotation.isExistAnnotation());


	FieldContainer fieldWinouthAnnotation = container.getAnnotedField("fieldWinouthAnnotation");
	assertEquals("fieldWinouthAnnotation", fieldWinouthAnnotation.getElementName());
	assertFalse(fieldWinouthAnnotation.isExistAnnotation());

	FieldContainer fieldWithAnnotation = container.getAnnotedField("fieldWithAnnotation");
	assertEquals("fieldWithAnnotation", fieldWithAnnotation.getElementName());
	assertTrue(fieldWithAnnotation.isExistAnnotation());
   }
}

Listas de Anotações Avançadas

Aqui está uma tabela com as anotações que são utilizadas para a leitura de metodos e atributos.

Anotação Elemento Alvo Uso da Anotação
@ProcessMethods Container de Classe Esta anotação de Fields verifica se existe a anotação selecionada no elemento alvo. Ele contem apenas o método value() que retorana se a anotação selecionada existe, esta anotação deve ser usada em tipos primitivos boolean ou classes do tipo Boolean.
@ProcessFields Container de Classe Esta anotação verifica se existe uma anotação anotada na classe alvo, e alem disso verifica, se esta existe uma propriedade com um nome descrito.
@AllMethodWith Container de Classe

Esta anotação pega as informações dos Methods, e coloca em um contêiner com as informações de Methods, deve ser armazenado em List, Set, Map de contêiner com informações sobre os methods. Ele contem apenas o método value() e somente pega os Methods com aquela anotação definida no mesmo.

@AllFieldsWith Container de Classe

Esta anotação pega as informações dos fields, e coloca em um contêiner de fields, deve ser armazenado em List, Set, Map de contêineres para os fields.Ele contem apenas o método value() e somente pega os fields com aquela anotação definida no mesmo.

@ElementProperty Container de Classe

Pega todos os property da classe e verifica se ele tem alguma anotação.


Processors

São utilizadas para a construção de esquemas personalizados de anotações para a sua aplicação utilizando a estrutura do Esfinge Metadata.

Para eles executarem nescessitam da configAnnotation, que vai uma classe, e do type que vai o ProcessorType

Os Processors são os seguintes, @CustonReader, @ProcessPerMethod, @ProcessPerField.
Os ProcessorType são os seguintes: READER_ADDS_METADATA, READER_IS_PROCESSOR, READER_RETURN_PROCESSOR.

Neste caso o Reader adiciona as informações de processors no container.


Exemplos:

Usando o @CustonReader e READER_ADDS_METADATA

Em primeiro Momento criaremos um Container, neste caso vai estar anotado nele o @CustonReader();

@ContainerFor(ContainerTarget.TYPE)
@CustomReader(configAnnotation = MyAnnotationReader.class, type = ProcessorType.READER_ADDS_METADATA,
 readerConfig = "value")
public class Container { private String className; //Get e Set omitidos }  


Anotação criada que será executada com o processor.

@Retention(RUNTIME)
public @interface MyAnnotationReader {
	Class<? extends InterfaceWithProcessor> value();
}


Anotação que vai na classe que queremos usar com o Esfinge Metadata com o processor.

@Retention(RUNTIME)
@MyAnnotationReader(ExecuteProcessor.class)
public @interface MyAnnotation {

}


Processor que vai ser executado, neste caso vai passar o nome do AnnotatedElement que vai no processor para a execução

public class ExecuteProcessor implements InterfaceWithProcessor {

	@Override
	public void execute(AnnotatedElement element,Object container) {
		((Container)container).setClassName(((Class)element).getName());
	}
}

Classe anotada com a anotação criada anteriormente.
@MyAnnotation
public class MyClass {

}


Metodo main que vai executar o programa, e retornar o nome da classe usando o READER_ADDS_METADATA.

public static void main(String[] args) throws Exception {
	AnnotationReader reader = new AnnotationReader();
	Container ct = new Container();
	ct = reader.readingAnnotationsTo(MyClass.class, Container.class);
	System.out.println(ct.getClassName());
}


Usando o @CustonReader e READER_IS_PROCESSOR

O reader é o elemento que contem as informações do processor.

Exemplo
Foi criado o container de anotações neste caso com o @CustonReader em uma lista, com a Interface PropertyProcessorInterface que vai ser criada futuramente, e com a configAnnotation que é responsavel por configurar a anotação que vai fazer o processamento das anotações de processor.

@ContainerFor(ContainerTarget.TYPE)
public class Container {

	@ElementName
	private String elementName;

	@CustomReader(configAnnotation=PropertyAnnotation.class, type = ProcessorType.READER_IS_PROCESSOR)
	private List<PropertyProcessorInterface> interf;
	//gets e sets omitidos
}

Anotação que usa a PropertyAnnotation,

Definição da property annotation, ela recebe uma classe que extende uma PropetyProcessorInterface, que você vai definir.

@Retention(RUNTIME)
@PropertyAnnotation(ExecuteProcessors.class)
public @interface OutraAnnotation {

}


Interface que foi criada para como um template de como deve ser o PropertyAnnotation.

public interface PropertyProcessorInterface {

	@ExecuteProcessor
	public void execute(Annotation ann, AnnotatedElement ael);

	public void print();
}


Classe que foi anotada na anotação @OutraAnnotation, está vai executar os metodos que na Interface estão anotados com @ExecuteProcessor, e ser impresso na tela o que estiver no metodo print().

@ContainerFor(ContainerTarget.ALL)
public class ExecuteProcessors implements PropertyProcessorInterface {

	private Annotation ann;
	private AnnotatedElement ael;

	@ContainsAnnotation(OutraAnnotation.class)
	private boolean existAnnotation;

 @Override public void execute(Annotation ann, AnnotatedElement ael) { this.ann = ann; this.ael = ael; } public boolean isExistAnnotation() { return existAnnotation; } public void setExistAnnotation(boolean existAnnotation) { this.existAnnotation = existAnnotation; } @Override public void print() { System.out.println("Elemento "+ael+" anotado com " + ann); System.out.println("A anotação "+ ann+ " "+existAnnotation); } }

A classe que foi anotada com a anotação @OutraAnotação.

@OutraAnnotation
public class ClassWithMetadata {

}

Aqui vemos o metodo main() de como é executado o Container com os processors, primeiro instancia um novo Container, e logo em seguida,

public static void main(String args[]) throws Exception {
	AnnotationReader ar = new AnnotationReader();

	Container ct = new Container();

	ct = ar.readingAnnotationsTo(ClassWithMetadata.class, Container.class);								for (PropertyProcessorInterface execute :ct.getInterf()) {
		execute.print();
	}


}
Usando o @CustonReader e READER_RETURN_PROCESSOR

Foi criado um contatainer, neste caso foi criado um container que retorna um tipo de container.

public class ContainerReaderReturn {

	@CustomReader(configAnnotation = AnnotationReturn.class, type= ProcessorType.READER_RETURNS_PROCESSOR,readerConfig="value")
	private List<ReturnInterface> list;

}

Anotação que vai ser configurada no @CustonReader.

public @interface AnnotationReturn {
    Class<? extends ReturnProcessorInterface> value();
}


Interface que foi criada para retornar os dados que vão ser retornados pelo container.

public interface ReturnInterface {
	public String returnDados();
}

Interface anotada com o @AnnotationReturn está vai chamar o reader e o reader vai chamar um metodo que vai popular o container.

@Retention(RUNTIME)
@AnnotationReturn(ProcessorAnnotation.class)
public @interface MeuReader {

}

Classe que implementa o ReturnProcessorInterface, está é a classe que vai executar o processor, e retornar a ReturnInterface.

public class ProcessorAnnotation implements ReturnProcessorInterface{
	@Override
	public ReturnInterface toReturn(Annotation an) {
		ImprimirAnotacao imprimir = new ImprimirAnotacao(an);
			return (ReturnInterface) imprimir;
	}
}

Classe que foi anotada com a anotação @MeuReader, está é que vai ser processada pelo Esfinge Metadata.
@MeuReader
public class ClassToRead {

}


Implementação da classe ImprimirAnotação, está é uma implementação da interface ReturnInterface, neste caso ela vai imprimir a anotação que foi passada para a classe.

public class ImprimirAnotacao implements ReturnInterface {
	private Annotation an;

	public ImprimirAnotacao(Annotation an) {
		this.an = an;
	}

	@Override
	public String returnDados() {
		// TODO Auto-generated method stub
		return "Anotação "+an.annotationType();
	}
}

Metodo main este vai pegar e executar o metadata e pegar as informações requeridas pelo container.

public static void main(String[] args) throws Exception {
		AnnotationReader ar = new AnnotationReader();
		ContainerReaderReturn ct = new ContainerReaderReturn();
		ct = ar.readingAnnotationsTo(ClassToRead.class, ct.getClass());

		System.out.println(ct.getElementName());
		for (ReturnInterface lista : ct.getList()) {
			System.out.println(lista.returnDados());
		}
	}
  

Exemplo de @ProcessorPerMethod e Reader_Is_Processor.


Em um primeiro momento criaremos o container com uma interface de processor, esta vai ser definida como:

@ContainerFor(ContainerTarget.TYPE)
public class Container{ @ProcessorPerMethod(configAnnotation=ProcessorAnnotation.class,type=ProcessorType.READER_IS_PROCESSOR) Map<Method,ProcessorInterface> map;
//gets e sets omitidos
 }


Criaremos a anotação que foi definida em configAnnotation, está será usada para configurar as anotações que serão usadas para dizer qual vai ser o processador da interface que vai ser utilizado no exemplo.

@Retention(RetentionPolicy.RUNTIME)
@SearchInsideAnnotations
@SearchOnEnclosingElements
@SearchOnAbstractions
public @interface ProcessorAnnotation {
	Class<? extends ProcessorInterface> value();
}


Criaremos a interface ProcessorInterface, está interface define como será executado o codigo dos processors.

public interface ProcessorInterface {

	@ExecuteProcessor
	public void processorInitializationFieldAndAnnotedElement(Annotation ann);

	public void returnDados();

}

Criaremos a uma anotação que denominanos @Entidade, está está anotada com o @ProcessorAnnotation() e é passado uma classe que implementa a interface ProcessorInterface, a anotação @SearchOnAbststractions esta define que a anotação pode estar nos elementos de nivel de Interface ou de Herança.
@Retention(RetentionPolicy.RUNTIME)
@SearchOnEnclosingElements
@SearchInsideAnnotations
@SearchOnAbstractions
@ProcessorAnnotation(Processor.class)
public @interface Entidade {

}

Implementação do metodo que vai processar conforme foi criado anteriormente.
public class Processor implements ProcessorInterface {

	private String field2;

	@Override
	public void processorInitializationFieldAndAnnotedElement(Annotation ann) {
		field2 = ann.annotationType().getName();
	}

	@Override
	public void returnDados() {
		System.out.println(field2);
	}
}


Classe que vai ser passada para a leitura de anotações, esta classe implementa a Interface DominioInterface, e possui uma implementação do metodo que queremos buscar com o Esfinge Metadata.

public class Dominio implements DominioInterface{

	@Override
	public void entidade1(){}

}


Interface criada com a anotação @Entidade, que por sua vez será buscada no metodo.

public interface DominioInterface {
  @Entidade public void entidade1(); }


Execução do EsfingeMetadata, neste caso ele verifica se a entidade está na classe ou na interface do mesmo.

public static void main(String args[]) throws Exception {
      Container container = new Container();
      AnnotationReader a1 = new AnnotationReader();
      container = a1.readingAnnotationsTo(Dominio.class, container.getClass());
      container.map.forEach((key, value) -> {
      value.returnDados();
   });
}


@ProcessorPerField


Exemplo:

Criaremos um container do tipo de field, este por sua vez está anotado com o @ProcessorPerField.

@ContainerFor(ContainerTarget.TYPE)
public class Container{ @ProcessorPerField(configAnnotation=ProcessorAnnotation.class,type=ProcessorType.READER_IS_PROCESSOR) Map<Field,ProcessorInterface> map;
//gets e sets omitidos
 }



Criaremos a anotação que foi definida em configAnnotation, está será usada para configurar as anotações que serão usadas para dizer qual vai ser o processador da interface que vai ser utilizado no exemplo.

@Retention(RetentionPolicy.RUNTIME)
@SearchInsideAnnotations
@SearchOnEnclosingElements
public @interface ProcessorAnnotation {
	Class<? extends ProcessorInterface> value();
}

Interface que vai ser setada com o map e ser passada a classe para a execução do processor interface.
public interface ProcessorInterface {
	@ExecuteProcessor
	public void processorInitializationAnnotation(Annotation ann);

	public void returnDados();

}

Implementação de uma anotação que tem a @ProcessorAnnotation() definida como ProcessorField.
@Retention(RetentionPolicy.RUNTIME)
@SearchOnEnclosingElements
@SearchInsideAnnotations
@SearchOnAbstractions
@ProcessorAnnotation(ProcessorField.class)
public @interface Entidade {
	String nome();
}

A classe ProcessorField vai pegar o valor da interface @Entidade que foi passado como parametro,
public class ProcessorField implements ProcessorInterface {

	private String field1;


	@Override
	public void processorInitializationAnnotation(Annotation ann) {
		field1 =((Entidade)ann).nome();
	}


	@Override
	public void returnDados() {
		// TODO Auto-generated method stub
		System.out.println(field1);
	}

}

Classe com varios fields anotados com a anotação @Entidade neste caso ele vai retornar a propriedade nome() da @Entidade.
public class Dominio{
	@Entidade(nome = "nome 1")
	private String field1;
	@Entidade(nome = "nome 2")
	private int field2;
}

Implementação do metodo main, ele vai retornar os dados que foram anotados na @Entidade na classe Dominio.
public static void main(String args[]) throws Exception {
   Container container = new Container();
   AnnotationReader a1 = new AnnotationReader();
 container = a1.readingAnnotationsTo(Dominio.class, container.getClass()); container.map.forEach((key, value) -> { value.returnDados(); }); }


Lista de Processors

Nesta seção falaremos sobre os processors, quais as suas funcionalidades e os atributus nescessarios para a a execução dos processors.


Lista de tipos de readers:
AnotaçãoElemento Alvo Uso da Anotação
@ExecuteProcessor Interface que vai ser executada pelos processors Dis que o metodo da interface será invocado por qualquer um dos processors, e define qual a ordem dos processors a ser executado por predefinição este valor é zero.
@ProcessorPerProperty Container de Classe Esta anotação processa as das anotações das propriedades, como metodos get ou atributos, quando não sabe-se em qual está sendo utilizado a anotação, passa como parametro o value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR.
@ProcessorPerField Container de Classe

Esta anotação processa somente as anotações dos Atributos da classe,  essa anotação tem que ser passada com os parametros value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR.

@ProcessorPerMethodContainer de Classe

Esta anotação processa somente as anotações dos Metodos da classe, essa anotação tem que ser passada com os parametros value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR.

@CustomReader Todos os containers

Procura anotações anotadas com a anotação passada como parametro na classe e esta anotação tem que ser passada com os parametros value() que é a interface que está os metodos e o type() que rescebe um dos ProcessorType, tendo como valor padrão ProcessorType.READER_IS_PROCESSOR.

Lista de Readers
Reader Funcionalidade
READER_ADDS_METADATA Neste caso o reader é o elemento responsável por popular o contêiner demetadados.
READER_RETURNS_PROCESSOR O reader retorna um objeto que foi definido como o processor.
READER_IS_PROCESSOR

O reader é o próprio processor.

Extensão do Metadata Reader

O esquema de anotação da própria API pode ser estendido. Se o framework precisar recuperar um metadados da classe que não é suportada pelas anotações API nativas, novas APIs de leitura de metadados podem ser definidas.

Para criar uma extensão do Metadata Reader é preciso:

Criar uma anotação anotada com @AnnotationReadingConfig() este arquivo vai dizer qual anotação está sendo passado;

Colocar a anotação @ValidFieldType() , esta anotação diz em que tipos ou classes as anotações serão anotadas. Exemplo:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@AnnotationReadingConfig(AllFieldsWithReadingProcessor.class)
@ValidFieldType({List.class, Set.class,Map.class})
public @interface AllFieldsWith {
   Class<? extends Annotation> value();
}

 Implemente a interface AnnotationReadingProcessor , ele tem essa estrutura:

public interface AnnotationReadingProcessor {
 void initAnnotation(Annotation an, Field field) throws AnnotationValidationException;
void read(AnnotatedElement classWithMetadata, Object container, ContainerTarget enumStr) throws AnnotationReadingException; }

Onde a função initAnnotation() é responsável pela inicialização da classe de validação e o read() é responsavel pela leitura das anotações:

Metadata Validator

Esta parte do framework é usada para validar se os elementos de anotação de codigo e verifica a integridade dos dados se estão compativeis com os parametros passados pelas anotações.

MetadataValidator em 1 minuto

Para executar o validador de metadados deve executar os seguintes passos:

  • Adicione o import org.esfinge.metadata.AnnotationValidationException, para chamar as exeções de validação do EsfingeMetadata:
  • Adicione o import org.esfinge.metadata.validate.MetadataValidator, para declarar que vai utilizar o EsfingeMetadata para validar o seu codigo:
  • Instancie o método: MetadataValidator.validateMetadataOn(); para procurar as anotações de validações do EsfingeMetadata, onde o Class<?> é a classe que se deseja validar:

Validação de Propriedade

Nesta seção falaremos sobre a validação de propriedades dos metodos, que são passados pelas anotações.

Em um primeiro momento criaremos uma anotação, que tem as seguintes anotações, @MinValue() que define o valor minimo que será passado como parametro da anotação, @MaxValue, o valor maximo que será passado como parametro da anotação e o @NotNull, que define que a anotação não pode ser NULL

@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD, ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PointsToUser {
   @MinValue(value=2)
   @MaxValue(value=10)
   int quantity();
   @NotNull
   String name();
}

Neste caso declaramos aqui a anotação com os parametros passados para um metodo, vemos aqui que o parametro quantitty é 5 e está entre os valores passados pelos parametros.

public class ValidAchevimentsPoints {
   @PointsToUser(quantity=5,name="ValidPoint")
   public void setPoints(){
   }
}
Executemos o MetadataValidator.validateMetadaOn() para validar as anotações, neste caso não vai cair em nenhuma exceção de validação.

@Test
public void validTestCase() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(ValidAchevimentsPoints.class);
}
Neste caso declaramos aqui a anotação com os parametros passados para um metodo, vemos aqui que o parametro quantitty é 0 e está abaixo do parametro anotado com a anotação @MinValue.

public class InvalidAchevimentsPoints {
   @PointsToUser(quantity=0,name="InvalidPoint")
   public void setPoints(){
   }
}

Executemos o MetadataValidator.validateMetadaOn() para validar as anotações, neste caso vai cair na exceção esperada, pois o valor está abaixo do setado pelo @MinValue.

@Test(expected=AnnotationValidationException.class)
public void invalidTestCase() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(InvalidAchevimentsPoints.class);
}


Aqui está uma lista de todas as anotações que podem ser usadas para a validação de propriedade.

Anotação Uso
@Unique Esta anotação de Fields verifica se existe a anotação selecionada no elemento alvo. Ele contem apenas o método value() que retorana se a anotação selecionada existe, esta anotação deve ser usada em tipos primitivos boolean ou classes do tipo Boolean.
@NotNull Esta anotação verifica se existe uma anotação anotada na classe alvo, e alem disso verifica, se esta existe uma propriedade com um nome descrito.
@MinValue Esta anotação de field pega o nome do elemento que foi anotado e passa para o field do container.
@MaxValue Esta anotação de field pega a classe do elemento que foi anotado e passa para o field que foi anotado com esse elemento.
@RefersTo Este validador indica que o valor do atributo de uma anotação deve referir-se (ser igual) ao valor do atributo de uma outra anotação em algum lugar no programa. O validador define os seguintes atributos: annotation(): A anotação alvo ao qual o atributo atual se refere. attributeValue(): O valor do atributo da anotação alvo ao qual o atributo atual se refere.

Validação de Contexto

Aqui são definidas as anotações de contexto, quais as anotações são permitidas ou proibidas de serem usadas.


Neste caso criaremos uma anotação denominada @PointsToUserValid() que é a classe que contem as anotações a serem validadas, eles tem anotada as anotações @NeedToHave que verifica se esta anotação contem a anotação passada como parametro, e a @Prohibits que diz que a anotação não deve ser passada em nenhum parametro da anotação.

@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD, ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@NeedsToHave(MaxValue.class)
@Prohibits(MinValue.class)
public @interface PointsToUserValid {
   @MaxValue(value=10)
   int quantity();
   @NotNull
   String name();
}


Criaremos uma anotação que tem um metodo que vai ser validado pela anotação criada anteriormente.


public class ValidAchevimentsPoints {
   @PointsToUserValid(quantity=5,name="ValidPoint")
   public void setPoints(){

   }
}


Executemos o MetadataValidator.validateMetadataOn(), neste caso estará executando corretamente.

@Test
public void validTestCase() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(PointsToUserValid.class);
}


Já neste caso, observamos que tem tambem a anotação @MinValue, que está proibida com a anotação @Prohibits(),

@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD, ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@NeedsToHave(MaxValue.class)
@Prohibits(MinValue.class)
public @interface PointsToUserInvalidNeedToHave {
   @MinValue(value=2)
   @MaxValue(value=10)
   int quantity();
   @NotNull
   String name();
}


Tambem foi criada uma classe com a anotação criada anteriormente.

public class AchevimentsPointsWithNeedToHaveExeption {
   @PointsToUserInvalidProhibits(quantity=0,name="InvalidPoint")
   public void setPoints()
   {
   }
}


Neste caso quando executar o MetadataValidator.validateMetadataOn() vai cair em uma exceção pois não está seguindo a regra do @Prohibits.

@Test(expected=AnnotationValidationException.class)
public void needToHaveTestCase() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(AchevimentsPointsWithNeedToHaveExeption.class);
}


Aqui está uma lista de todas as anotações que podem ser usadas para a validação de contexto.

Anotação Uso
@Prohibits Este validador estabelece que se um elemento de código é anotado com a meta-anotação atual, então, ela proibirá a utilização de outra anotação informada. Este validator é definido por um único atributo value() informando a classe da anotação a ser proibida.
@NeedsToHave Este validador é o opostodo @Prohibits.Ele requer que a meta-anotação atual deve ocorrer em elementos que contenham a anotação informada no value().
 

Validação de Campo

 

Validação por Modificadores


Em primeiro momento criaremos uma anotação anotada com @StaticFieldOnly, neste caso foi nomeada @OneAnnotationWithStaticFieldOnly, ela vai verificar se o atributo é estatico.
@SearchInsideAnnotations
@SearchOnEnclosingElements
@StaticFieldOnly
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithStaticFieldOnly {
}

Uma classe com o atributo estatico name anotado com @OneAnnotationWithStaticFieldOnly

public class StaticFieldValid {
   @OneAnnotationWithStaticFieldOnly
   private static String name;
}
Criaremos aqui um caso de teste valido, pois o atributo anotado criado anteriormente, é estatico.
@Test
public void testValidModyfers() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(StaticFieldValid.class);
}

Aqui estamos vendo um caso com erro, pois o elemento anotado com a anotação @OneAnnotationWithStaticFieldOnly, não é um atributo estatico e portanto vai ocasionar uma exeção.

public class StaticFieldInvalid{
   @OneAnnotationWithStaticFieldOnly
   protected String nameInvalid;
}				

Criaremos aqui um caso de teste que vai retornar a exceção AnnnotationValidationExeption, pois possui um atributo invalido na classe StaticFieldInvalid.

@Test(expected=AnnotationValidationException.class)
public void testInvalidModyfers() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(StaticFieldInvalid.class);
}				

Anotação Uso
@FinalFieldOnly Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter o modificador final.
@StaticFieldOnly Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter o modificador static.
@TransientFieldOnly Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter o modificador transient.
@VolatileFieldOnly Esta anotação de field pega a classe do elemento que foi anotado e passa para o field que foi anotado com esse elemento.
@InstanceFieldOnly Esta meta-anotação indica que o campo que utiliza a anotação anotada NÃO pode ter o modificador static.

Validação por Visibilidade

Nesta seção falaremos sobre as validações de visibilidade, e verificamos se os atributos são visiveis ou proibidos no codigo.

Para esse exemplo criaremos uma anotação chamada @OneAnnotationWithFieldVisibilityRequired, ela contem a seguinte anotação do EsfingeMetadata. @FieldVisibilityRequired, que tem como parametro a visibilidade do atributo, neste caso o atributo deve ser privado.

@SearchInsideAnnotations
@SearchOnEnclosingElements
@FieldVisibilityRequired(itNeedsToHaveThisVisibility = "private")
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithFieldVisibilityRequired {
}


Criaremos uma classe e anotamos o atributo name, com a anotação criada anteriormente, e vemos que o tipo do atributo é o mesmo da que foi passado na anotação criada anteriormente.

public class VisibilityRequeridValid {
   @OneAnnotationWithFieldVisibilityRequired
   private String name;
}


Executamos um caso de teste, para verificar se o EsfingeMetadata valida corretamente esse parametro.

@Test
public void testValidVisibility() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(VisibilityRequeridValid.class);
}

Neste caso temos um elemento com visibilidade protected, este difere da visibilidade requirida na validação.

public class VisibilityRequeridInvalid {
   @OneAnnotationWithFieldVisibilityRequired
   protected String name;
}

Aqui está o exemplo de validação com anotação invalida, neste caso está esperado a exceção AnnotationValidationExcepition, pois neste caso está esperando um atributo com a visibilidade private mas está recebendo um atributo com a visibilidade protected.

@Test(expected=AnnotationValidationException.class)
   public void testInvalidVisibility() throws AnnotationValidationException {
   MetadataValidator.validateMetadataOn(VisibilityRequeridInvalid.class);
}


Aqui está a listas de anotações que podem ser utilizados para validação de visibilidade.

Anotação Uso
@FieldVisibilityForbidden Esta meta-anotação indica que o campo que utiliza a anotação anotada NÃO pode ter a visibilidade passada no parâmetro itCannotHaveThisVisibility.
@FieldVisibilityRequired Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter a visibilidade passada no parâmetro itNeedsToHaveThisVisibility.
Validação por Tipo

Aqui falaremos sobre validadores de tipo para atributos, nesta seção tem somente a anotação @ValidFieldTypes.

Criaremos uma anotação anotada com @ValidFIeldType, que recebe como parametro listValidTypes que é um array de classes, este define quais tipos são permitidos de existir no atributo, neste caso é String, List.

@SearchInsideAnnotations
@SearchOnEnclosingElements
@ValidFieldTypes(listValidTypes = {String.class, List.class})
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface OneAnnotationWithValidFieldTypes {
}

Aqui criamos uma classe com o atributo a ser validado, neste caso vai ser validado pois o tipo que está sendo passado é do mesmo tipo que foi passado previamente para a anotação @OneAnnotationWithValidFieldTypes.

public class TypeFieldValid {
   @OneAnnotationWithValidFieldTypes
   private String name;
}

Neste caso vai ocorrer uma exceção pois o atributo não passa nenhum dos tipos definido na anotaçaõ @OneAnnotationWithValidFieldTypes.

public class TypeFieldInvalid{
	@OneAnnotationWithValidFieldTypes
	private int invalid;
}

Aqui tem um exemplo com os dois comportamentos que podem ocorrer na execução dessa validação, o validTypeExamples() ocorre quando não tem nenhum erro de validação de propriedades, enquanto o invalidTypeExamples() vai disparar a exeção que esta sendo experada.

public class TypeExamples {

   @Test
   public void validTypeExamples() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(TypeFieldValid.class);
   }

@Test(expected=AnnotationValidationException.class) public void invalidTypeExamples() throws AnnotationValidationException { MetadataValidator.validateMetadataOn(TypeFieldInvalid.class); } }


Anotação Uso
@ValidFieldTypes Esta meta-anotação indica que o campo que utiliza a anotação anotada precisa ter um tipo correspondente ao passado no parâmetro listValidTypes.

Validação de Método

 Nesta seção vai ser falado sobre a validação de contexto por metodos, e quais são as anotações que foram criadas para o uso deles, alem de um exemplo basico de uso.

Validação por Modificadores

Em um primeiro momento criaremos uma anotação chamada @OneAnnotationWithFinalMethodOnly, que contem a anotação @FinalMethodOnly que vai ser responsavel por dizer que o metodo que essa classe está anotada é final.

@FinalMethodOnly
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithFinalMethodOnly {
}


Exemplo de um metodo anotado com a anotação criada anteriormente, este é a versão correta de dizer que um metodo é valido, conforme o  parametro criado anteriormente.

public class FinalMethodOnlyValid {
   @OneAnnotationWithFinalMethodOnly
   public final void finalValid(){
   }
}


Exemplo de um metodo anotado com a anotação criada anteriormente, este é invalido, pois o metodo anotado não é do tipo final.

import net.sf.esfinge.metadata.examples.validate.method.test.OneAnnotationWithFinalMethodOnly;

public class FinalMethodOnlyInvalid {
   
@OneAnnotationWithFinalMethodOnly public void finalInvalid(){} }

Exemplo de caso de teste para validação dos metodos, o caso modifersValid() contem a anotação valida, enquanto o modifersInvalid() contem a anotação que retorna a exceção requirida.

public class ModifersTest
   @Test
   public void modifersValid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(FinalMethodOnlyValid.class);
   }
   @Test(expected=AnnotationValidationException.class)
   public void modifersInvalid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(FinalMethodOnlyInvalid.class);
   }
}

Aqui a lista de anotações que podem ser usados para validação por modificadores de Metodos.

Anotação Uso
@FinalMethodOnly Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador final.
@StaticMethodOnly Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador static.
@SyncronizedMethodOnly Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador synchronized.
@StrictfpMethodOnly Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o modificador strictfp.
@InstanceMethodOnly Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter o modificador static.

Validação por Visibilidade


Exemplo de @MethodVisibilityForbidden, neste caso dis qual o tipo que o metodo não pode ser, neste caso o metodo não pode ser privado.

@MethodVisibilityForbidden(itCannotHaveThisVisibility = "private")
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithMethodVisibilityForbidden {
}

Aqui um exemplo valido, repare que neste caso o metodo não é privado.

public class VisibilityForbidenValid {
   @OneAnnotationWithMethodVisibilityForbidden
   public double someCalc(double a, double b){
	return Math.pow(( a * b ) * 4.2, 5);
   }
}

Exemplo que pode ocorrer uma exceção, o metodo é privado.

public class VisibilityForbidenInvalid {
   @OneAnnotationWithMethodVisibilityForbidden
   private double someCalc(double a, double b){
      return Math.pow(( a * b ) * 4.2, 5);
   }
}

Aqui está um caso de teste que faz a verificação das classes criadas para a validação, repare que o testVisibilityValid() valida a anotação, enquanto o testVisibilityInvalid() retorna uma anotação que contem exceção apresentada.

public class ValidateVisibility {
   @Test
   public void testVisibilityValid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(VisibilityForbidenValid.class);
   }

@Test(expected=AnnotationValidationException.class) public void testVisibilityInvalid() throws AnnotationValidationException { MetadataValidator.validateMetadataOn(VisibilityForbidenInvalid.class); } }


Anotação Uso
@MethodVisibilityForbidden Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter a visibilidade passada no parâmetro itCannotHaveThisVisibility.
@MethodVisibilityRequired Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter a visibilidade passada no parâmetro itNeedsToHaveThisVisibility.

Validação por Convenção de Nome

Em primeiro momento criaremos uma anotação que contenha a anotação @MethodNamingConvention, esta recebe como parametro um REGEX com o nome do metodo.
@MethodNamingConvention(regexNamingConvencion = "^get")
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithMethodNamingConvention {
}

Criaremos uma classe valida usando a anotação criada anteriormente, neste caso vai executar com sucesso pois o metodo começa com o REGEX ^get.

public class NamingConvectionTrue {
   @OneAnnotationWithMethodNamingConvention
   public void getName(){
   }
}

Neste caso, não vai estar correto pois o nome do metodo não contem o REGEX ^get.

public class NamingConvectionFalse {
   @OneAnnotationWithMethodNamingConvention
   public void setName(){
   }
}
Aqui está o codigo de teste, ele vai retornar as exeções de codigo para o caso que não satisfaz a validação, e não vai retornar nada no codigo que satisfaz a validação.
public class NamingConvectionTest {
   @Test
   public void NamingConvectionValid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(NamingConvectionTrue.class);
   }
   @Test(expected=AnnotationValidationException.class)
   public void NamingConvectionInvalid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(NamingConvectionFalse.class);
   }
}


Anotação Uso
@MethodNammingConvention Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter um nome de método de acordo com a expressão regular passada no parâmetro regexNamingConvencion.

Validação por Parâmetros


Em um primeiro momento criaremos uma anotação que tem anotada o @ValidNumbersOfParameters, neste caso o valor é 2.

@ValidNumberOfParameters(numberOfParameters = 2)
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithValidNumberOfParameters {
}

Criaremos uma classe com os numeros parametros validos pela anotação criada anteriormente.

public class ParameterForbidenValid {
   @OneAnnotationWithValidNumberOfParameters
   public double someCalc(double a, double b){
      return Math.pow(( a * b ) * 4.2, 5);
   }
}

Aqui vai ser criada uma classe com os parametros invalidos.

public class ParameterForbidenInvalid {
   @OneAnnotationWithValidNumberOfParameters
   private strictfp double someCalc(double a){
      return Math.pow(( a ) * 4.2, 5);
   }
}

Criaremos o caso de teste para a verificação do uso com anotação valida e anotação invalida.

public class ParameterVisibility {
   @Test
   public void testVisibilityValid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(ParameterForbidenValid.class);
   }

   @Test(expected=AnnotationValidationException.class)
   public void testVisibilityInvalid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(ParameterForbidenInvalid.class);
   }
}

Listas de anotação de validação por parametros.

Anotação Uso
@NoParameterMethodOnly Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter parâmetros.
@ValidNunberOfParameters Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter a quantidade de parâmetros passado no parâmetro numberOfParameters.
@ValidMethodParameterTypes Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter os parâmetros correspondentes ao que foi passado no parâmetro validParameters, onde os parâmetros válidos são indicados pela anotação @Parameters no parâmetro parameters.

Validação por Retorno
Para este caso vamos criar uma anotação anotaca com @ValidMethodReturn, que é responsavel por dizer qual o retorno esperado para determinado metodo, neste caso do exemplo é int.class ou Integer.class.
@ValidMethodReturn(validTypesToReturn = {int.class, Integer.class})
@Retention(RetentionPolicy.RUNTIME)
public @interface OneAnnotationWithValidMethodReturn {
}

Criaremos uma classe com um metodo anotado com a anotação criada anteriormente, neste caso é valido pois está retornando um tipo inteiro.

public class ReturnValid {
   @OneAnnotationWithValidMethodReturn
   public int someCalc(int a, int b){
      return a+b;
   }
}

Criaremos uma classe com um metodo anotado com a anotação criada anteriormente,  neste caso é invalido pois está retornando uma string.

public class ReturnInvalid {
   @OneAnnotationWithValidMethodReturn
   private String someCalc(double a){
      return "INVALID";
   }
}

Aqui a execução do caso de teste, repare que quando se testa o caso invalido ele cai em uma exceção de AnnotationValidationException.

public class ParameterVisibility {
    @Test
   public void testVisibilityValid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(ReturnValid.class);
   }
@Test(expected=AnnotationValidationException.class)
   public void testVisibilityInvalid() throws AnnotationValidationException {
      MetadataValidator.validateMetadataOn(ReturnInvalid.class);
   }
}




Anotação Uso
@ForbiddenMethodReturn Esta meta-anotação indica que o método que utiliza a anotação anotada NÃO pode ter o retorno passado no parâmetro invalidTypesToReturn.
@ValidMethodReturn Esta meta-anotação indica que o método que utiliza a anotação anotada precisa ter o retorno passado no parâmetro validTypesToReturn.

Extensão do Metadata Validator

Para criar uma extensão para o validador de metadados é necessário:

Criar uma anotação anotada com: @ToValidateProperty()

Exemplo:

@ToValidateProperty(validationClass = UniqueAnnotationValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Unique {
}

Implemente o AnnotationPropertyValidator, ele tem essa estrutura:

public interface AnnotationPropertyValidator {
    public void initialize(Annotation self);
    public void validate(Annotation annotationOnElement, AnnotatedElement annotatedWithMainAnnotation, Method annotatedWithValidation, Object annotationPropertyValue) throws AnnotationValidationException;
}

Onde o método initialize() é responsável pela inicialização da classe de validação e o validate() é responsavel pela validação;

Segue um exemplo da implementação da Anotação Unique.

 public class UniqueAnnotationValidator implements AnnotationPropertyValidator {

	private List<Annotation> annotationList = new ArrayList<Annotation>();

	@Override
	public void initialize(Annotation self) {

	}

	@Override
	public void validate(Annotation annotationOnElement,
			AnnotatedElement annotatedWithMainAnnotation,
			Method annotatedWithValidation, Object annotationPropertyValue)
			throws AnnotationValidationException {

		Method[] methods = annotationOnElement.annotationType().getMethods();

		for (Method m : methods) {
			for(Annotation an : m.getAnnotations()){
				if(an instanceof Unique){
					annotationList.add(an);
				}
			}
		}

		if(annotationList.size() > 1){
			throw new
			AnnotationValidationException("The @"+annotationOnElement.annotationType().getSimpleName()+" must have only one field annotated with @Unique!");
		}else if (annotationList.size() == 0 ) {
			throw new AnnotationValidationException("The annotation @"
					+ Unique.class.getSimpleName().toString()
					+ " was not found");
		}
	}
}

 Aqui criaremos um exemplo de uso desta anotação, em um primeiro momento criaremos uma anotação, esta anotação possue dois atributos anotado com @Unique, sendo que neste caso somente deve ter uma anotação anotada com @Unique.

@SearchOnEnclosingElements
@SearchInsideAnnotations
@Target({ElementType.METHOD,ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PointsToUser {
	@Unique
	int quantity();
	@Unique
	String name();
}
Aqui foi criado um caso de teste que vai retornar a AnnotationValidationExeption, pois a anotação a ser validada é invalida de acordo com o funcionamento da anotação @Unique.
@Test(expected = AnnotationValidationException.class)
public void VerifiyIsUnique() throws AnnotationValidationException, IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, SecurityException { MetadataValidator.validateMetadataOn(Classe.class); }

 Tambem no Metadata Validator possui outro ponto para a extenção do Metadata Validator a Interface AnnotationValidator.

Aqui um exemplo de uso desta anotação.

Em primeiro momento criaremos uma anotação anotada com @ToValidate(), esta tem como parametro a implementação da interface AnnotationValidator.
@ToValidate(value = ProhibitsAnnotationValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Prohibits {
   public Class<? extends Annotation> value();
}

Em seguida criaremos o a implementação do validador.

public class ProhibitsAnnotationValidator implements AnnotationValidator{

   protected Class<? extends Annotation> whatItNeedsToProhibits;

   @Override
   public void initialize(Annotation self) {
      Prohibits ntp = (Prohibits) self;
      whatItNeedsToProhibits = ntp.value();
   }

   @Override
   public void validate(Annotation toValidate, AnnotatedElement annotated) throws AnnotationValidationException {
      if(AnnotationFinder.findAnnotation(annotated, whatItNeedsToProhibits).size() > 0){
         throw new AnnotationValidationException("The annotation it needs to prohibits was found = " 
+ whatItNeedsToProhibits.toString()); } } }


Criamos então duas anotações que implementa a anotação @Prohibits, esta recebe uma anotação que [não deve ser colocada de nenhuma maneira no projeto, cada uma proibe a outra.

@Prohibits(OptimizeExecution01.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging01 {
}

@SearchOnEnclosingElements @SearchInsideAnnotations @Prohibits(Logging01.class) @Retention(RetentionPolicy.RUNTIME) public @interface OptimizeExecution01 { }

Em seguida criaremos o teste das classes, vai acionar a AnnotationValidationExeption pois esta possui as anotações proibidas de executar.

public class OrderProcessing02 {
   @Logging01
   @OptimizeExecution01
   public void registerPurchase(Purchase p) {
   }
}

@Test(expected = AnnotationValidationException.class)
public void CTWithExeption() throws AnnotationValidationException {
	MetadataValidator.validateMetadataOn(OrderProcessing02.class);
}


Apoio

Todos os direitos reservados