Quando bem utilizado, esse recurso pode executar tarefas extremamente arrojadas.
Requisito:
Represar todos os métodos DAOs em uma sessão web, e executá-los todos de uma vez dentro de uma transação ao final da sessão
JVM 5 + Spring + Jetty 1.6
Solução:
Interceptando todas as chamadas dos métodos DAOs, armazeno uma sequência de invocações em uma Collection em um Statefull Session Bean, de forma que fiquem represados em memória.
StatefullBeanInterface:
addCallStack(Object target, String methodName, Object... args);
ExampleDAO:
@Transactional(propagation = Propagation.MANDATORY)
addUser(User user) {
// real persistence operation
}
addUser(StatefullBean s, User user) {
// interceptor operation
s.addCallStack(this, "addUser", user);
}
Ao final da sessão, o usuário confirma a execução dos métodos. Agora entra em ação a Reflection.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void execCallStack() throws CallStackException {
for (Object o : callStack.keySet()) {
List<MethodCall> call = callStack.get(o);
for (MethodCall method : call) {
Object[] args = method.getArgs();
Method[] allMethods = o.getClass().getDeclaredMethods();
for (Method m : allMethods) {
if (m.getName().equals(method.getMethodName()) && m.getGenericParameterTypes().length == args.length) {
try {
m.invoke(o, args);
break;
} catch (Exception e) {
throw new CallStackException(e);
}
}
}
}
}
}
Passo a passo:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void execCallStack()throws CallStackException {
Solicitamos ao container que crie uma nova transação ao iniciar a execução dos métodos DAO represados.
Os métodos são anotados com propagação de transação mandatória (Propagation.MANDATORY). Dessa forma, se forem invocados foram de uma transação, uma exceção será gerada.
for (Object o : callStack.keySet()) {
List<MethodCall> call = callStack.get(o);
for (MethodCall method : call) {
Object[] args = method.getArgs();
Estamos percorrendo a Collection com os objetos alvo da invocação, o nome dos métodos e seus respectivos argumentos.
Method[] allMethods = o.getClass().getDeclaredMethods();
for (Method m : allMethods) {
Aqui começa a reflection. getDeclaredMethods retorna todos os métodos de uma classe. A classe Method encapsula suas declarações, como argumentos, exceções, encapsulamento, entre outros.
if (m.getName().equals(method.getMethodName()) && m.getGenericParameterTypes().length == args.length) {
Procuramos o método correspondente através do nome e a quantidade de argumentos, para minimizar eventuais problemas com sobrecarga.
Checagens adicionais podem ser feitas, porém no caso acima não houve necessidade.
m.invoke(o, args);
Finalmente o método invoke executa o código. O primeiro argumento é uma instância Object que implemente a Classe alvo, e o segundo argumento é um vetor com os métodos.
A CallStackException é uma exceção Runtime criada especificamente para essa situação, para efetuar o rollback das operações em banco em caso de algum método falhar.
Mais informações:
http://java.sun.com/docs/books/tutorial/reflect/
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
2 comentários:
Meu problema com reflection é que o código costuma não ser muito legível. Se tiver um tempo, de uma olhada em http://projetos.vidageek.net/mirror-pt . É uma DSL para trabalhar com reflection.
Postar um comentário