Package org.springmodules.jcr

Source Code of org.springmodules.jcr.TransactionAwareRepository$TransactionAwareRepositoryInvocationHandler

/**
* Created on Nov 11, 2005
*
* $Id: TransactionAwareRepository.java,v 1.4 2008/01/29 12:28:24 coliny Exp $
* $Revision: 1.4 $
*/
package org.springmodules.jcr;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.jcr.Credentials;
import javax.jcr.Repository;
import javax.jcr.Session;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

/**
* This FactoryBean exposes a proxy for a target JCR Repository,
* returning the current thread-bound Repository (the Spring-managed
* transactional Repository or the single OpenPersistenceManagerInView
* Repository) on <code>login()</code>, if any.
*
* <p/>Essentially, <code>login()</code> calls get seamlessly
* forwarded to <code>SessionFactoryUtils.getSession</code>.
* Furthermore, <code>Session.logout</code> calls get forwarded to
* <code>SessionFactoryUtils.releaseSession</code>.
* <p/>As the Session returned depends on the workspace and credentials given,
* this implementation accepts a JcrSessionFactory as parameter (basically a
* wrapper for Repository, Credentials and Workspace properties). The proxy
* will check the parameters and proxy the Repository for sessions that are
* retrieved with the credentials and workspace name defined on the session factory.
* Sessions retrieved with different workspace, credentials are not proxied.
*
* <p/>The main advantage of this proxy is that it allows DAOs to work with a
* plain JCR Repository reference, while still participating in
* Spring's (or a J2EE server's) resource and transaction management. DAOs will
* only rely on the JCR API in such a scenario, without any Spring dependencies.
*
* DAOs could seamlessly switch between a JNDI
* Repository and this proxy for a local Repository
* receiving the reference through Dependency Injection. This will work without
* any Spring API dependencies in the DAO code!
*
* <p/>It is usually preferable to write your JCR-based DAOs with Spring Modules's
* JcrTemplate, offering benefits such as consistent data access exceptions
* instead of RepositoryExceptions at the DAO layer. However, Spring's resource and
* transaction management (and Dependency Injection) will work for DAOs
* written against the plain JCR API too.
*
* <p/>Of course, you can still access the target Repository
* even when your DAOs go through this proxy, by defining a bean reference
* that points directly at your target Repository bean.
*
* @author Costin Leau
*
*/
public class TransactionAwareRepository implements InitializingBean, FactoryBean {

  private JcrSessionFactory sessionFactory;
  private Repository proxy;
  private SessionHolderProviderManager sessionHolderProviderManager;

  /**
   * allow creation of sessions if none is found on the current thread.
   */
  private boolean allowCreate = true;

  /**
   * allow creation of repository even if it doesn't support transactions.
   */
  private boolean allowNonTxRepository = false;

  /**
   * @see org.springframework.beans.factory.FactoryBean#getObject()
   */
  public Object getObject() throws Exception {
    return proxy;
  }

  /**
   * @see org.springframework.beans.factory.FactoryBean#getObjectType()
   */
  public Class getObjectType() {
    return Repository.class;
  }

  /**
   * @see org.springframework.beans.factory.FactoryBean#isSingleton()
   */
  public boolean isSingleton() {
    return true;
  }

  /**
   * @return Returns the allowCreate.
   */
  public boolean isAllowCreate() {
    return allowCreate;
  }
 
  /**
   * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
   */
  public void afterPropertiesSet() throws Exception {
    if (sessionFactory == null)
      throw new IllegalArgumentException("sessionFactory is required");
    // the rest of the properties are set by the setTargetFactory
    if (!allowNonTxRepository && !JcrUtils.supportsTransactions(sessionFactory.getRepository()))
      throw new IllegalArgumentException(sessionFactory.toString() + " does NOT support transactions and allowNonTxRepository is false");
  }

  /**
   * Set whether the Repository proxy is allowed to create
   * a non-transactional Session when no transactional
   * Session can be found for the current thread.
   * <p>Default is "true". Can be turned off to enforce access to
   * transactional Sessionss, which safely allows for DAOs
   * written to get a Session without explicit closing
   * (i.e. a <code>Session.login()</code>
   * call without corresponding <code>Session.logout()</code> call).
   *
   * @see SessionFactoryUtils#getSession(SessionFactory, boolean)
   * @param allowCreate The allowCreate to set.
   */
  public void setAllowCreate(boolean allowCreate) {
    this.allowCreate = allowCreate;
  }

  /**
   * @return Returns the allowNonTxRepository.
   */
  public boolean isAllowNonTxRepository() {
    return allowNonTxRepository;
  }

  /**
   * Set whether the Repository proxy is accepted even if it does not
   * support transactions which allows the functionality of thread-bound
   * session but without the tx support. Such an option exists because
   * transaction support is optional for JSR-170 implementations.
   *
   * <p>Default is "true". Can be turned off to enforce only
   * transactional Sessionss, which safely allows for DAOs
   * written to get a Session without explicit closing
   * (i.e. a <code>Session.login()</code>
   * call without corresponding <code>Session.logout()</code> call).
   *
   * @param allowNonTxRepository The allowNonTxRepository to set.
   */
  public void setAllowNonTxRepository(boolean allowNonTxRepository) {
    this.allowNonTxRepository = allowNonTxRepository;
  }

  /**
   * Set the target JCR Repository that this proxy should
   * delegate to wrapped in a JcrSessionFactory object along
   * with the credentials and workspace.
   *
   */
  public void setTargetFactory(JcrSessionFactory target) {
    this.sessionFactory = target;
    this.proxy = (Repository) Proxy.newProxyInstance(Repository.class.getClassLoader(),
        new Class[] { Repository.class }, new TransactionAwareRepositoryInvocationHandler());
  }

  /**
   * Return the target JCR Repository that this proxy delegates to.
   */
  public Repository getTargetRepository() {
    return this.sessionFactory.getRepository();
  }

  /**
   * Invocation handler that delegates login calls on the Repository proxy to SessionFactoryUtils
   * for being aware of thread-bound transactions.
   */
  private class TransactionAwareRepositoryInvocationHandler implements InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      // Invocation on Repository interface coming in...
      // check method invocation
      if (method.getName().equals("login")) {
        boolean matched = false;

        // check method signature
        Class[] paramTypes = method.getParameterTypes();

        // a. login()
        if (paramTypes.length == 0) {
          // match the sessionFactory definitions
          matched = (sessionFactory.getWorkspaceName() == null && sessionFactory.getCredentials() == null);
        }
        else if (paramTypes.length == 1) {
          // b. login(java.lang.String workspaceName)
          if (paramTypes[0] == String.class)
            matched = ObjectUtils.nullSafeEquals(args[0], sessionFactory.getWorkspaceName());
          // c. login(Credentials credentials)
          if (Credentials.class.isAssignableFrom(paramTypes[0]))
            matched = ObjectUtils.nullSafeEquals(args[0], sessionFactory.getCredentials());
        }
        else if (paramTypes.length == 2) {
          // d. login(Credentials credentials, java.lang.String workspaceName)
          matched = ObjectUtils.nullSafeEquals(args[0], sessionFactory.getCredentials())
              && ObjectUtils.nullSafeEquals(args[1], sessionFactory.getWorkspaceName());
        }

        if (matched) {
          Session session = SessionFactoryUtils.getSession(sessionFactory, isAllowCreate());
          Class[] ifcs = ClassUtils.getAllInterfaces(session);
          return (Session) Proxy.newProxyInstance(getClass().getClassLoader(), ifcs,
              new TransactionAwareInvocationHandler(session, sessionFactory));
        }
      }
      else if (method.getName().equals("equals")) {
        // Only consider equal when proxies are identical.
        return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
      }
      else if (method.getName().equals("hashCode")) {
        // Use hashCode of Repository proxy.
        return new Integer(hashCode());
      }

      Repository target = getTargetRepository();

      // Invoke method on target Repository.
      try {
        return method.invoke(target, args);
      }
      catch (InvocationTargetException ex) {
        throw ex.getTargetException();
      }
    }
  }

  /**
   * Invocation handler that delegates close calls on Sessions to
   * SessionFactoryUtils for being aware of thread-bound transactions.
   */
  private static class TransactionAwareInvocationHandler implements InvocationHandler {

    private final Session target;
    private final SessionFactory sessionFactory;

    public TransactionAwareInvocationHandler(Session target, SessionFactory sessionFactory) {
      this.target = target;
      this.sessionFactory = sessionFactory;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // Invocation on Session interface coming in...

      if (method.getName().equals("equals")) {
        // Only consider equal when proxies are identical.
        return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
      }
      else if (method.getName().equals("hashCode")) {
        // Use hashCode of Session proxy.
        return new Integer(hashCode());
      }
      else if (method.getName().equals("logout")) {
        // Handle close method: only close if not within a transaction.
        if (this.sessionFactory != null) {
          SessionFactoryUtils.releaseSession(this.target, this.sessionFactory);
        }
        return null;
      }

      // Invoke method on target Session.
      try {
        return method.invoke(this.target, args);
      }
      catch (InvocationTargetException ex) {
        throw ex.getTargetException();
      }
    }
  }
}
TOP

Related Classes of org.springmodules.jcr.TransactionAwareRepository$TransactionAwareRepositoryInvocationHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.