| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.felix.ipojo.transaction; |
| |
| import java.lang.reflect.Member; |
| import java.lang.reflect.Method; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import javax.transaction.HeuristicMixedException; |
| import javax.transaction.HeuristicRollbackException; |
| import javax.transaction.InvalidTransactionException; |
| import javax.transaction.NotSupportedException; |
| import javax.transaction.RollbackException; |
| import javax.transaction.SystemException; |
| import javax.transaction.Transaction; |
| import javax.transaction.TransactionManager; |
| |
| import org.apache.felix.ipojo.MethodInterceptor; |
| |
| public class TransactionalMethod implements MethodInterceptor { |
| |
| public static final int REQUIRES = 0; |
| |
| public static final int REQUIRES_NEW = 1; |
| |
| public static final int MANDATORY = 2; |
| |
| public static final int SUPPORTED = 3; |
| |
| public static final int NOT_SUPPORTED = 4; |
| |
| public static final int NEVER = 5; |
| |
| private String method; |
| |
| private int propagation; |
| |
| private int timeout; |
| |
| private List<String> exceptions; |
| |
| private TransactionManager manager; |
| |
| private Map <Thread, Transaction> m_owned = new HashMap<Thread, Transaction>(); |
| |
| private boolean exceptionOnRollback; |
| |
| |
| private TransactionHandler handler; |
| |
| private Transaction suspended; |
| |
| public TransactionalMethod(String method, int propagation, int timeout, List<String> exception, boolean exceptionOnRollback, TransactionHandler handler) { |
| this.method = method; |
| this.propagation = propagation; |
| this.timeout = timeout; |
| this.exceptions = exception; |
| this.exceptionOnRollback = exceptionOnRollback; |
| this.handler = handler; |
| } |
| |
| public synchronized void setTransactionManager(TransactionManager tm) { |
| manager = tm; |
| if (manager == null) { |
| // Clear stored transactions. |
| m_owned.clear(); |
| |
| } |
| } |
| |
| |
| public void onEntry() throws SystemException, NotSupportedException { |
| TransactionManager manager = null; |
| synchronized (this) { |
| if (this.manager != null) { |
| manager = this.manager; // Stack confinement |
| } else { |
| return; // Nothing can be done... |
| } |
| } |
| |
| Transaction transaction = manager.getTransaction(); |
| switch (propagation) { |
| case REQUIRES: |
| // Are we already in a transaction? |
| if (transaction == null) { |
| // No, create one |
| if (timeout > 0) { |
| manager.setTransactionTimeout(timeout); |
| } |
| manager.begin(); |
| m_owned.put(Thread.currentThread(), manager.getTransaction()); |
| } else { |
| // Add the transaction to the transaction list |
| handler.addTransaction(transaction); |
| } |
| break; |
| case MANDATORY: |
| if (transaction == null) { |
| // Error |
| throw new IllegalStateException("The method " + method + " must be called inside a transaction"); |
| } else { |
| // Add the transaction to the transaction list |
| handler.addTransaction(transaction); |
| } |
| break; |
| case SUPPORTED: |
| // if transaction != null, register the callback, else do nothing |
| if (transaction != null) { |
| handler.addTransaction(transaction); |
| } // Else do nothing. |
| break; |
| case NOT_SUPPORTED: |
| // Do nothing. |
| break; |
| case NEVER: |
| if (transaction != null) { |
| throw new IllegalStateException("The method " + method + " must never be called inside a transaction"); |
| } |
| break; |
| case REQUIRES_NEW: |
| if (transaction == null) { |
| // No current transaction, Just creates a new one |
| if (timeout > 0) { |
| manager.setTransactionTimeout(timeout); |
| } |
| manager.begin(); |
| m_owned.put(Thread.currentThread(), manager.getTransaction()); |
| } else { |
| if (suspended == null) { |
| suspended = manager.suspend(); |
| if (timeout > 0) { |
| manager.setTransactionTimeout(timeout); |
| } |
| manager.begin(); |
| m_owned.put(Thread.currentThread(), manager.getTransaction()); |
| } else { |
| throw new IllegalStateException("The method " + method + " requires to suspend a second times a transaction"); |
| } |
| } |
| break; |
| default: |
| throw new UnsupportedOperationException("Unknown or unsupported propagation policy for " + method + " :" + propagation); |
| |
| } |
| } |
| |
| public void onExit() throws SecurityException, HeuristicMixedException, HeuristicRollbackException, SystemException, InvalidTransactionException, IllegalStateException { |
| switch (propagation) { |
| case REQUIRES: |
| // Are we the owner of the transaction? |
| Transaction transaction = m_owned.get(Thread.currentThread()); |
| if (transaction != null) { // Owner. |
| try { |
| transaction.commit(); // Commit the transaction |
| m_owned.remove(Thread.currentThread()); |
| handler.transactionCommitted(transaction); // Manage potential notification. |
| } catch ( RollbackException e) { |
| m_owned.remove(Thread.currentThread()); |
| // The transaction was rolledback |
| if (exceptionOnRollback) { |
| throw new IllegalStateException("The transaction was rolled back : " + e.getMessage()); |
| } |
| handler.transactionRolledback(transaction); // Manage potential notification. |
| } |
| } // Else wait for commit. |
| break; |
| case MANDATORY: |
| // We are never the owner, so just exits the transaction. |
| break; |
| case SUPPORTED: |
| // Do nothing. |
| break; |
| case NOT_SUPPORTED: |
| // Do nothing. |
| break; |
| case NEVER: |
| // Do nothing. |
| break; |
| case REQUIRES_NEW: |
| // We're necessary the owner. |
| transaction = m_owned.get(Thread.currentThread()); |
| if (transaction == null) { |
| throw new RuntimeException("Cannot apply the REQUIRES NEW propagation, we're not the transaction owner!"); |
| } |
| try { |
| transaction.commit(); // Commit the transaction |
| m_owned.remove(Thread.currentThread()); |
| handler.transactionCommitted(transaction); // Manage potential notification. |
| if (suspended != null) { |
| manager.suspend(); // suspend the completed transaction. |
| manager.resume(suspended); |
| suspended = null; |
| } |
| } catch ( RollbackException e) { // The transaction was rolledback rather than committed |
| m_owned.remove(Thread.currentThread()); |
| if (suspended != null) { |
| manager.suspend(); // suspend the completed transaction. |
| manager.resume(suspended); // The resume transaction is not rolledback, they are independent. |
| suspended = null; |
| } |
| // The transaction was rolledback |
| if (exceptionOnRollback) { |
| throw new IllegalStateException("The transaction was rolled back : " + e.getMessage()); |
| } |
| handler.transactionRolledback(transaction); // Manage potential notification. |
| } |
| break; |
| default: |
| throw new UnsupportedOperationException("Unknown or unsupported propagation policy for " + method + " :" + propagation); |
| |
| } |
| } |
| |
| public void onError(String exception) throws SystemException { |
| TransactionManager manager = null; |
| synchronized (this) { |
| if (this.manager != null) { |
| manager = this.manager; // Stack confinement |
| } else { |
| return; // Nothing can be done... |
| } |
| } |
| |
| // is the error something to exclude, and are we inside the transaction (owner or participant)? |
| if (! exceptions.contains(exception)) { |
| Transaction tr = manager.getTransaction(); |
| if (m_owned.containsValue(tr) || handler.getTransactions().contains(tr)) { |
| // Set the transaction to rollback only |
| manager.getTransaction().setRollbackOnly(); |
| } |
| } |
| } |
| |
| public void onEntry(Object o, Member member, Object[] objects) { |
| try { |
| onEntry(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| throw new RuntimeException("An issue occurs during transaction management of " + method + " : " + e.getMessage()); |
| } |
| |
| } |
| |
| public void onError(Object o, Member member, Throwable throwable) { |
| try { |
| throwable.printStackTrace(); |
| |
| onError(throwable.getClass().getName()); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| throw new RuntimeException("An issue occurs during transaction management of " + method + " : " + e.getMessage()); |
| } |
| } |
| |
| public void onExit(Object o, Member member, Object o1) { |
| // Wait for on finally. |
| } |
| |
| public void onFinally(Object o, Member member) { |
| try { |
| onExit(); |
| } catch (IllegalStateException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new RuntimeException("An issue occurs during transaction management of " + method + " : " + e.getMessage()); |
| } |
| } |
| |
| public void rollbackOwnedTransactions() { |
| Iterator<Entry<Thread, Transaction>> entries = m_owned.entrySet().iterator(); |
| while(entries.hasNext()) { |
| Entry<Thread, Transaction> entry = entries.next(); |
| try { |
| entry.getValue().rollback(); |
| } catch (IllegalStateException e) { |
| throw new RuntimeException("An issue occurs during transaction management of " + method + " : " + e.getMessage()); |
| } catch (SystemException e) { |
| throw new RuntimeException("An issue occurs during transaction management of " + method + " : " + e.getMessage()); |
| } |
| } |
| m_owned.clear(); |
| } |
| |
| } |