/*
* ============================================================================
* GNU Lesser General Public License
* ============================================================================
*
* Beanlet - JSE Application Container.
* Copyright (C) 2006 Leon van Zantvoort
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Leon van Zantvoort
* 243 Acalanes Drive #11
* Sunnyvale, CA 94086
* USA
*
* zantvoort@users.sourceforge.net
* http://beanlet.org
*/
package org.beanlet.plugin;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Enumeration;
import javax.transaction.Transaction;
import org.beanlet.BeanletApplicationException;
import org.jargo.ComponentReference;
/**
* Provides a transaction local scope for objects.
*
* @author Leon van Zantvoort
*/
public class TransactionLocal<T> {
private static class LazyHolder {
static final Constructor<TransactionLocalDelegate> local;
static {
try {
try {
Constructor constructor = AccessController.doPrivileged(
new PrivilegedExceptionAction<Constructor>() {
public Constructor run() throws Exception {
String path = "META-INF/services/" +
TransactionLocal.class.getName();
// PERMISSION: java.lang.RuntimePermission getClassLoader
ClassLoader loader = Thread.currentThread().
getContextClassLoader();
final Enumeration<URL> urls;
if (loader == null) {
urls = TransactionLocal.class.
getClassLoader().getResources(path);
} else {
urls = loader.getResources(path);
}
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
BufferedReader reader = new BufferedReader(new InputStreamReader(
url.openStream()));
try {
String className = null;
while ((className = reader.readLine()) != null) {
final String name = className.trim();
if (!name.startsWith("#") && !name.startsWith(";") &&
!name.startsWith("//")) {
final Class<?> cls;
if (loader == null) {
cls = Class.forName(name);
} else {
cls = Class.forName(name, true, loader);
}
int m = cls.getModifiers();
if (TransactionLocalDelegate.class.isAssignableFrom(cls) &&
!Modifier.isAbstract(m) &&
!Modifier.isInterface(m)) {
// PERMISSION: java.lang.RuntimePermission accessDeclaredMembers
Constructor constructor = cls.getDeclaredConstructor();
// PERMISSION: java.lang.reflect.ReflectPermission suppressAccessChecks
if (!Modifier.isPublic(constructor.getModifiers())) {
constructor.setAccessible(true);
}
return constructor;
} else {
throw new ClassCastException(cls.getName());
}
}
}
} finally {
reader.close();
}
}
throw new BeanletApplicationException("No " +
"TransactionLocalDelegate implementation " +
"found.");
}
});
@SuppressWarnings("unchecked")
Constructor<TransactionLocalDelegate> tmp =
(Constructor<TransactionLocalDelegate>) constructor;
local = tmp;
} catch (PrivilegedActionException e) {
throw e.getException();
}
} catch (BeanletApplicationException e) {
throw e;
} catch (RuntimeException e) {
throw new BeanletApplicationException(e);
} catch (Error e) {
throw e;
} catch (Throwable t) {
throw new BeanletApplicationException(t);
}
}
}
private final TransactionLocalDelegate<T> delegate;
public TransactionLocal() {
try {
try {
@SuppressWarnings("unchecked")
TransactionLocalDelegate<T> tmp = (TransactionLocalDelegate<T>)
LazyHolder.local.newInstance();
delegate = tmp;
} catch (ExceptionInInitializerError e) {
try {
throw e.getException();
} catch (Throwable t) {
throw new BeanletApplicationException(t);
}
}
} catch (BeanletApplicationException e) {
throw e;
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new BeanletApplicationException(e);
}
}
/**
* Subclasses can override this method to return a value if no value is
* currently set.
*/
protected T initialValue() {
return null;
}
/**
* Returns transaction that is associated with the calling thread, ]
* {@code null} otherwise. The returned transaction is not necessarily
* active.
*
* @return the transaction associated with the calling thread.
*/
public final Transaction getTransaction() {
return delegate.getTransaction();
}
// /**
// * Returns {@code true} if there is a transaction associated with the
// * calling thread. This does not automatically mean that this transaction
// * is also active.
// */
// public final boolean isInTransaction() {
// return getTransaction() != null;
// }
/**
* Sets the specified {@code value}. This value can be retrieved by
* {@code this.get()} only in the context of the transaction that is
* associated with the calling thread.
*
* @param value value to be assigned to scoped transaction, or {@code null}
* to remove current value.
* @throws IllegalStateException if not in transactional scope.
*/
public final void set(T value) throws IllegalStateException {
delegate.set(value);
}
/**
* Returns the value that has been set on this {@code TransactionLocal}
* instance for the transaction that is associated with the calling thread.
*
* @throws IllegalStateException if not in transactional scope.
*/
public final T get() throws IllegalStateException {
T value = delegate.get();
return value == null ? initialValue() : value;
}
/**
* Removes the value that is bound to the transaction associated with the
* calling thread. This method is equal to {@code set(null)}.
*
* @throws IllegalStateException if not in transactional scope.
*/
public final void remove() throws IllegalStateException {
delegate.remove();
}
/**
* Callback interface is invoked when a user transaction is started by the
* specified {@code reference}.
*/
public final void onUserTransaction(ComponentReference<?> reference,
Runnable callback) {
delegate.onUserTransaction(reference.weakReference(), callback);
}
}