package org.superbiz.accesstimeout.api;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Metatype
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Metatype {
}
@AccessTimeout a forma Meta-anotação
Indrodução
Qualquer anotação que recebe parâmetros podem se beneficiar das meta-anotações. Aquí observamos como @AccessTimeout
pode ser mais fácil de entender e administrar mediante meta-anotações.
Usaremos [access-timeout](../access-timeout/README.html) como nosso exemplo de caso de uso.
O valor dos parâmetros do fornecido para o @AccessTimeout
tem um efeito dramático em que a anotação realmente faz. Além disso, @ AccessTimeout
tem um desses designs
onde -1
e 0
tem um significado totalmente diferente. Um quer dizer "espere para sempre", o outro "nunca espere". Só alguns com sorte podem lembrar qual é qual.
Para o resto de nós é uma fonte constante de erros.
Meta-Anotações. ao resgate! |
Criando Meta-Anotações.
Com respeito as melhores práticas, colocaremos nossas meta-anotações em um pacote chamado api
, para este exemplo que nós da org.superbiz.accesstimeout.api
. O pacote org.superbiz.api
também vai funcionar.
A ideia básica é ter um pacote onde anotações "aprovadas" sejam usadas e proibir uso de versões não-meta-anotações. Toda a configuração
estará centralizada no pacote api
e alterações nos valores de tempo limite estarão localizadas nesse pacote e refletidas automaticamente em todo o aplicativo.
Um efeito secundário interessante desta abordagem é que se o pacote api
onde as definições de meta-anotação existem localizados em um jar separado, então efetivamente alguém
pode trocar toda a configuração de uma aplicação simplesmente substituindo o jar api
.
@Metatype A Meta-Anotação "root"
Como todo o uso de meta-anotação, primeiro devemos criar nossa própria meta-anotação "root". Isto é tão facil como criar uma anotação
chamada Metatype
que esta anotada com esta mesma anotação e tem um ElementType.ANNOTATION_TYPE
como seu objetivo.
@AwaitNever
Quando a anotação @AccessTimeout
tem o valor de 0
que tem a implicação que nunca deve esperar para acessar o bean. Se o bean esta ocupado, aquele que o chama imediatamente
receberá uma ConcurrentAccessException
. Isto é difícil de lembrar e definitivamente não é auto-documentado para aqueles que nunca conheceram os detalhes.
Para criar uma versão de meta-anotação @AccessTimeout(0)
nós simplesmente devemos pensar um bom nome de anotação, criar essa anotação e anota-la com ambas @AccessTimeout
e @Metatype
package org.superbiz.accesstimeout.api;
import javax.ejb.AccessTimeout;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Metatype
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@AccessTimeout(0)
public @interface AwaitNever {
}
@AwaitForever
Assim como 0
tem seu significado especial como "nunca esperar" , o valor de -1
quer dizer "espere para sempre."
Enquanto estamos exigindo, o que podemos fazer com meta-anotações,
tecnicamente "espere para sempre" não é a melhor descrição. Atualmente os métodos de javax.util.concurrent
APIs usam "await" em vez de "wait". Um (wait) provavelmente implica
um comando de espera, que neste caso não é , o outro (await) em vez disso, significa que esperar é possível, mas não é certo. Então, usaremos "await" no nome de nossas anotações.
Nós fazemos o nosso próprio @AwaitForever
e a anotamos com @AccessTimeout(0)
e @Metatype
package org.superbiz.accesstimeout.api;
import javax.ejb.AccessTimeout;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Metatype
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@AccessTimeout(-1)
public @interface AwaitForever {
}
@AwaitBriefly
Tanto os valores de -1
e 0
a @AccessTimeout
eles não incluem o escopo completo da anotação. Aqui é onde podemos especificar o máximo de minutos , segundos,
milisegundos,etc. onde se pode esperar acessar a instância do bean.
@Metatype
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@AccessTimeout(value = 5, unit = TimeUnit.SECONDS)
public @interface AwaitBriefly {
}
Configuração vs Operação
Depois de criar algumas meta-anotações e a diversão se tornar vulgar, você começa a se perguntar qual será a melhor maneira de se beneficiar das meta-anotações.
Você realmente tem que começar a pensar como você quer usar a meta-anotação e colocar o chapéu de designer. O dilema fundamental é configuração vs operação e a resposta é subjetiva; Quanta flexibilidade você deseja projetar em sua aplicação e onde?
Nomes de configuração descrevendo a configuração
A abordagem mais simples é chamar suas meta-anotações depois da configuração que encapsulam. Nós seguimos este formato até agora com @AwaitNever
e @AwaitForever
para que seja claro o conteúdo de cada meta-anotação (@AccessTimeout(-1)
e @AccessTimeout(0)
respectivamente).
Os contras desta abordagem é que você vai querer alterar as configurações deste aplicativo apenas alterando as meta-anotações — este é um dos grandes beneficios
das meta-anotações — mas isto podería trocar o significado da anotação. Certamente , a anotação @AwaitNever
não pode ter outro valor que '0' se estiver na altura do nome.
Nomes de operação descrevendo o código
A abordagem alternativa é chamar as meta-anotações depois de operações a que se aplica. Brevemente descrever, descrever o próprio código e não a configuração. Assim que,
nomes como @OrderCheckTimeout
ou @TwitterUpdateTimeout
. Estes nomes são provas de troca de configuração. Isso não mudará se a configuração mudar e, de fato, eles podem facilitar o controle de
localizador de grãos sobre a configuração do aplicativo.
Os contras desta abordagem é que requer muito mais deliberação e consideração, se, mencionar mais anotações. Suas habilidades como arquiteto, designer e capacidade de pensar como administrador serão postas a prova. Você tem que ser bom para usar o chapéu dev-ops.
Pragmatismo o melhor dos dois mundos
Felizmente, as meta-anotações são recursivas. Você pode fazer um pouco dos dois.
@Metatype
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@AwaitBriefly
public @interface TwitterUpdateTimeout {
}
Claro ainda temos que ser muito deliberado em como usar as anotações. Quando se usa uma "configuração" chamada meta-anotação no código pode ser usada para dizer a si mesmo "Você não quer reconfigurá-lo depois". Se isso não parecer certo, faça um esforço extra para criar uma operação chamada anotação e use-a no código.
Aplicando a Meta-Anotação
Juntando tudo , talvez assim é como deveríamos aplicar nossas meta-anotações para o exemplo [access-timeout](../access-timeout/README.html).
Antes
package org.superbiz.accesstimeout;
import javax.ejb.AccessTimeout;
import javax.ejb.Asynchronous;
import javax.ejb.Lock;
import javax.ejb.Singleton;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static javax.ejb.LockType.WRITE;
/**
* @version $Revision$ $Date$
*/
@Singleton
@Lock(WRITE)
public class BusyBee {
@Asynchronous
public Future stayBusy(CountDownLatch ready) {
ready.countDown();
try {
new CountDownLatch(1).await();
} catch (InterruptedException e) {
Thread.interrupted();
}
return null;
}
@AccessTimeout(0)
public void doItNow() {
// do something
}
@AccessTimeout(value = 5, unit = TimeUnit.SECONDS)
public void doItSoon() {
// do something
}
@AccessTimeout(-1)
public void justDoIt() {
// do something
}
}
Depois
package org.superbiz.accesstimeout;
import org.superbiz.accesstimeout.api.AwaitBriefly;
import org.superbiz.accesstimeout.api.AwaitForever;
import org.superbiz.accesstimeout.api.AwaitNever;
import javax.ejb.Asynchronous;
import javax.ejb.Lock;
import javax.ejb.Singleton;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import static javax.ejb.LockType.WRITE;
/**
* @version $Revision$ $Date$
*/
@Singleton
@Lock(WRITE)
public class BusyBee {
@Asynchronous
public Future stayBusy(CountDownLatch ready) {
ready.countDown();
try {
new CountDownLatch(1).await();
} catch (InterruptedException e) {
Thread.interrupted();
}
return null;
}
@AwaitNever
public void doItNow() {
// do something
}
@AwaitBriefly
public void doItSoon() {
// do something
}
@AwaitForever
public void justDoIt() {
// do something
}
}