/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/amber/entity/AmberEntityHome.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.amber.entity;

import com.caucho.amber.AmberException;
import com.caucho.amber.AmberRuntimeException;
import com.caucho.amber.manager.AmberConnection;
import com.caucho.amber.manager.AmberPersistenceUnit;
import com.caucho.amber.query.CacheUpdate;
import com.caucho.amber.type.*;
import com.caucho.config.ConfigException;
import com.caucho.util.L10N;

import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.lang.Comparable;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Manages the set of persistent beans.
 */
public class AmberEntityHome implements Comparable {
  private static final L10N L = new L10N(AmberEntityHome.class);
  private static final Logger log
    = Logger.getLogger(AmberEntityHome.class.getName());

  private AmberPersistenceUnit _manager;
  private EntityType _entityType;

  private EntityFactory _entityFactory = new EntityFactory();

  private Entity _homeBean;

  private ArrayList<SoftReference<CacheUpdate>> _cacheUpdates
    = new ArrayList<SoftReference<CacheUpdate>>();

  private volatile boolean _isInit;

  private RuntimeException _configException;

  private Method _cauchoGetBeanMethod;

  public AmberEntityHome(AmberPersistenceUnit manager, EntityType type)
  {
    _manager = manager;
    _entityType = type;
  }

  /**
   * Returns the getBean method to instantiate the Amber object.
   */
  public Method getCauchoGetBeanMethod()
  {
    return _cauchoGetBeanMethod;
  }

  /**
   * Returns the manager.
   */
  public AmberPersistenceUnit getManager()
  {
    return _manager;
  }

  /**
   * Returns the entity type
   */
  public EntityType getEntityType()
  {
    return _entityType;
  }

  /**
   * Returns the entity type
   */
  public EntityType getRootType()
  {
    return (EntityType) _entityType.getRootType();
  }

  /**
   * Returns the java class.
   */
  public Class getJavaClass()
  {
    return _entityType.getInstanceClass();
  }

  /**
   * Returns the entity factory.
   */
  public EntityFactory getEntityFactory()
  {
    return _entityFactory;
  }

  /**
   * Sets the entity factory.
   */
  public void setEntityFactory(EntityFactory factory)
  {
    _entityFactory = factory;
  }

  /**
   * Returns the cache timeout.
   */
  public long getCacheTimeout()
  {
    return _entityType.getCacheTimeout();
  }

  /**
   * Returns the instance class.
   */
  public Class getInstanceClass()
  {
    return _entityType.getInstanceClass();
  }

  /**
   * Link the classes.
   */
  void link()
    throws ConfigException
  {
    // _entityClass.link(_manager);
  }

  /**
   * Initialize the home.
   */
  public void init()
    throws ConfigException
  {
    synchronized (this) {
      if (_isInit)
        return;
      _isInit = true;
    }

    _entityType.init();

    try {
      Class instanceClass = _entityType.getInstanceClass();

      if (! Modifier.isAbstract(instanceClass.getModifiers()))
        _homeBean = (Entity) instanceClass.newInstance();
    } catch (Exception e) {
      _entityType.setConfigException(e);

      _configException = ConfigException.create(e);
      throw _configException;
    }

    _entityType.start();
  }

  /**
   * Returns the entity from the key.
   */
  public Object getKeyFromEntity(Entity entity)
    throws AmberException
  {
    //    return _entityType.getId().getType().getValue(obj);
    return null;
  }

  /**
   * Converts a long key to the key.
   */
  public Object toObjectKey(long key)
  {
    return _entityType.getId().toObjectKey(key);
  }

  /**
   * Finds by the primary key.
   */
  public EntityItem findItem(AmberConnection aConn,
                             ResultSet rs, int index)
    throws SQLException
  {
    EntityItem item = _homeBean.__caucho_home_find(aConn, this, rs, index);

    return item;
  }

  /**
   * Finds by the primary key.
   */
  public Object loadFull(AmberConnection aConn, ResultSet rs, int index)
    throws SQLException
  {
    // jpa/0l43
    Entity entity;

    if (_homeBean == null)
      throw new NullPointerException("HOME:" + this);

    entity = aConn.getSubEntity(_homeBean.getClass(),
                                rs.getObject(index));

    if (entity != null) {
      if (entity.__caucho_getEntityState().isManaged())
        return entity;
    }

    EntityItem item = findItem(aConn, rs, index);

    if (item == null)
      return null;

    entity = null;

    Object value = aConn.getEntityLazy(item);

    if (aConn.isActiveTransaction()) {
      if (value instanceof Entity)
        entity = (Entity) value;
      else if (_cauchoGetBeanMethod != null) {
        try {
          entity = (Entity) _cauchoGetBeanMethod.invoke(value, new Object[0]);
          entity.__caucho_makePersistent(aConn, item);
        } catch (Exception e) {
          log.log(Level.FINER, e.toString(), e);
        }
      }

      if (entity == null)
        entity = aConn.getEntity(item);
    }
    else
      entity = item.getEntity();

    int keyLength = _entityType.getId().getKeyCount();

    entity.__caucho_load(aConn, rs, index + keyLength);

    return entity;
  }

  /**
   * Finds by the primary key.
   */
  public Object loadLazy(AmberConnection aConn, ResultSet rs, int index)
    throws SQLException
  {
    EntityItem item = findItem(aConn, rs, index);

    if (item == null)
      return null;

    return aConn.getEntity(item);
  }
  
  public EntityItem findEntityItem(AmberConnection aConn, Object key)
    throws AmberException
  {
    if (_homeBean == null && _configException != null)
      throw _configException;

    Entity entity;

    entity = (Entity) _homeBean.__caucho_home_find(aConn, this, key);

    // jpa/0l43 - with inheritance, an XA entity creates XAEntityItem
    // to avoid double loading
    if (entity == null)
      return null;
    else if (aConn.isActiveTransaction())
      return new XAEntityItem(this, entity);
    else
      return new CacheableEntityItem(this, entity);
  }

  /**
   * Loads an entity based on the primary key.
   *
   * @param key the primary key
   * @param aConn the Amber connection to associate with the loaded item
   * @param isLoad if true, try to load the bean
   */
  public EntityItem setEntityItem(Object key, EntityItem item)
    throws AmberException
  {
    if (key == null)
      throw new NullPointerException("primaryKey");

    try {
      item.getEntity().__caucho_setConnection(_manager.getCacheConnection());

      return _manager.putEntity(getRootType(), key, item);
    } catch (Exception e) {
      throw AmberException.create(e);
    }
  }

  /**
   * Loads an entity where the type is determined by a discriminator
   *
   * @param aConn the connection to associate with the entity
   * @param key the primary key
   * @param discriminator the object's discriminator
   */
  public EntityItem findDiscriminatorEntityItem(AmberConnection aConn,
                                                Object key,
                                                String discriminator)
    throws SQLException
  {
    EntityItem item = null;

    // jpa/0l20
    // XXX: ejb/0d01
    // if (aConn.shouldRetrieveFromCache())
    item = _manager.getEntity(getRootType(), key);

    if (item == null) {
      EntityType subEntity
        = (EntityType) _entityType.getSubClass(discriminator);

      Entity cacheEntity = subEntity.createBean();

      cacheEntity.__caucho_setPrimaryKey(key);
      cacheEntity.__caucho_makePersistent(_manager.getCacheConnection(),
                                          subEntity);

      item = new CacheableEntityItem(this, cacheEntity);

      // The cache entity is added after commit.
      if (! aConn.isActiveTransaction()) {
        item = _manager.putEntity(getRootType(), key, item);
      }
    }

    return item;
  }

  /**
   * Instantiates a new entity for this home.
   */
  public Entity newEntity(Object key)
  {
    return (Entity) _homeBean.__caucho_home_new(this, key, null, null);
  }

  /**
   * Loads an entity where the type is determined by a discriminator
   *
   * @param aConn the connection to associate with the entity
   * @param key the primary key
   * @param discriminator the object's discriminator
   */
  public Entity newDiscriminatorEntity(Object key,
                                       String discriminator)
  {
    if (discriminator == null || key == null)
      throw new AmberRuntimeException(L.l("{0} is not a valid inheritance key.",
                                          key));

    EntityType subType = (EntityType) _entityType.getSubClass(discriminator);

    return subType.getHome().newEntity(key);
  }

  /**
   * Finds by the primary key.
   */
  public Entity makePersistent(Entity entity,
                               AmberConnection aConn,
                               boolean isLazy)
    throws SQLException
  {
    entity.__caucho_makePersistent(aConn, _entityType);

    return entity;
  }

  /**
   * Saves based on the object.
   */
  public void save(AmberConnection aConn, Entity entity)
    throws SQLException
  {
    // Common to JPA and CMP.
    entity.__caucho_lazy_create(aConn, _entityType);

    if (! _manager.isJPA())
      entity.__caucho_create(aConn, _entityType);
  }

  /**
   * Deletes by the primary key.
   */
  public void delete(AmberConnection aConn, Object key)
    throws SQLException
  {
    _manager.removeEntity(getRootType(), key);
  }

  /**
   * Deletes by the primary key.
   */
  public void delete(AmberConnection aConn, long primaryKey)
    throws SQLException
  {
  }

  /**
   * Update for a modification.
   */
  public void update(Entity entity)
    throws SQLException
  {
  }

  /**
   * Adds a cache update.
   */
  public void addUpdate(CacheUpdate update)
  {
    _cacheUpdates.add(new SoftReference<CacheUpdate>(update));
  }

  public int compareTo(Object b)
  {
    AmberEntityHome home = (AmberEntityHome) b;

    return _entityType.getClassName().compareTo(home.getEntityType().getClassName());
  }

  public String toString()
  {
    return "AmberEntityHome[" + _entityType + "]";
  }
}
