org.batoo.jpa.core.impl.manager.EntityManagerFactoryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.batoo.jpa.core.impl.manager.EntityManagerFactoryImpl.java

Source

/*
 * Copyright (c) 2012-2013, Batu Alp Ceylan
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */

package org.batoo.jpa.core.impl.manager;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

import javax.persistence.Cache;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceException;
import javax.persistence.PersistenceUnitUtil;
import javax.persistence.Query;
import javax.sql.DataSource;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;

import org.apache.commons.lang.StringUtils;
import org.batoo.common.BatooException;
import org.batoo.common.BatooVersion;
import org.batoo.common.log.BLogger;
import org.batoo.common.log.BLoggerFactory;
import org.batoo.common.util.BatooUtils;
import org.batoo.jpa.BJPASettings;
import org.batoo.jpa.JPASettings;
import org.batoo.jpa.core.impl.criteria.CriteriaBuilderImpl;
import org.batoo.jpa.core.impl.criteria.QueryImpl;
import org.batoo.jpa.core.impl.criteria.jpql.JpqlQuery;
import org.batoo.jpa.core.impl.deployment.DdlManager;
import org.batoo.jpa.core.impl.deployment.LinkManager;
import org.batoo.jpa.core.impl.deployment.NamedQueriesManager;
import org.batoo.jpa.core.impl.model.MetamodelImpl;
import org.batoo.jpa.jdbc.AbstractDataSource;
import org.batoo.jpa.jdbc.BoneCPDataSource;
import org.batoo.jpa.jdbc.DDLMode;
import org.batoo.jpa.jdbc.DataSourceProxy;
import org.batoo.jpa.jdbc.PreparedStatementProxy.SqlLoggingType;
import org.batoo.jpa.jdbc.adapter.AbstractJdbcAdaptor;
import org.batoo.jpa.jdbc.adapter.JdbcAdaptor;
import org.batoo.jpa.parser.AbstractLocator;
import org.batoo.jpa.parser.PersistenceParser;
import org.batoo.jpa.parser.metadata.NamedQueryMetadata;

import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.jolbox.bonecp.BoneCP;

/**
 * Implementation of {@link EntityManagerFactory}.
 * 
 * @author hceylan
 * @since 2.0.0
 */
public class EntityManagerFactoryImpl implements EntityManagerFactory, Serializable {
    private static final long serialVersionUID = BatooVersion.SERIAL_VERSION_UID;

    private static final BLogger LOG = BLoggerFactory.getLogger(EntityManagerFactoryImpl.class);

    private static final int NO_QUERIES_MAX = 1000;
    private static final int NO_QUERIES_TRIM = 100;

    private final MetamodelImpl metamodel;
    private final DDLMode ddlMode;

    private final DataSourceProxy dataSource;

    private final JdbcAdaptor jdbcAdaptor;
    private final Map<String, Object> properties = Maps.newHashMap();
    private final Map<String, JpqlQuery> namedQueries = Maps.newHashMap();
    private final CriteriaBuilderImpl criteriaBuilder;
    private final PersistenceUnitUtilImpl persistenceUtil;

    private final HashMap<String, JpqlQuery> jpqlCache = Maps.newHashMap();
    private final ClassLoader classloader;

    private final ValidatorFactory validationFactory;
    private final Class<?>[] persistValidators;
    private final Class<?>[] updateValidators;
    private final Class<?>[] removeValidators;

    private final int maxFetchJoinDepth;

    private boolean open;

    private AbstractDataSource dataSourcePool;

    /**
     * @param name
     *            the name of the entity manager factory
     * @param parser
     *            the persistence parser
     * 
     * @since 2.0.0
     */
    public EntityManagerFactoryImpl(String name, PersistenceParser parser) {
        super();

        this.classloader = parser.getClassloader();
        this.prepareProperties(parser);

        final boolean hasValidators = parser.hasValidators();
        if (hasValidators) {
            this.validationFactory = this.createValidationFactory();
            this.persistValidators = this.getValidatorsFor(parser, JPASettings.PERSIST_VALIDATION_GROUP);
            this.updateValidators = this.getValidatorsFor(parser, JPASettings.UPDATE_VALIDATION_GROUP);
            this.removeValidators = this.getValidatorsFor(parser, JPASettings.REMOVE_VALIDATION_GROUP);
        } else {
            this.validationFactory = null;
            this.persistValidators = null;
            this.updateValidators = null;
            this.removeValidators = null;
        }

        try {
            this.maxFetchJoinDepth = this.getProperty(BJPASettings.MAX_FETCH_JOIN_DEPTH) != null ? //
                    Integer.valueOf(((String) this.getProperty(BJPASettings.MAX_FETCH_JOIN_DEPTH))) : //
                    BJPASettings.DEFAULT_MAX_FETCH_JOIN_DEPTH;
        } catch (final Exception e) {
            throw new IllegalArgumentException("Illegal value " + this.getProperty(BJPASettings.SQL_LOGGING)
                    + " for " + BJPASettings.SQL_LOGGING);
        }

        this.dataSource = this.createDatasource(name, parser);

        this.ddlMode = this.readDdlMode();

        this.jdbcAdaptor = this.createJdbcAdaptor();
        this.metamodel = new MetamodelImpl(this, this.jdbcAdaptor, parser.getMetadata());

        LinkManager.perform(this.metamodel);

        this.metamodel.checkTables();

        // drop all tables if ddl mode is drop
        if (this.ddlMode == DDLMode.DROP) {
            this.metamodel.dropAllTables(this.dataSource);
        }

        DdlManager.perform(this.dataSource, this.metamodel, this.ddlMode);

        this.metamodel.performSequencesDdl(this.dataSource, this.ddlMode);
        this.metamodel.performTableGeneratorsDdl(this.dataSource, this.ddlMode);

        this.metamodel.preFillGenerators(this.dataSource);
        this.criteriaBuilder = new CriteriaBuilderImpl(this.metamodel);

        NamedQueriesManager.perform(this.metamodel, this.criteriaBuilder);

        // lets init static metamodel class if exist
        this.metamodel.initStaticMetamodels();
        //

        this.persistenceUtil = new PersistenceUnitUtilImpl(this);

        this.jdbcAdaptor.importSql(this.classloader, this.dataSource,
                (String) this.getProperties().get(BJPASettings.IMPORT_SQL));

        BatooUtils.gaBoot(this.properties);
        this.open = true;
    }

    /**
     * Adds the named query to the entity manager factory.
     * 
     * @param name
     *            the name of the query
     * @param jpqlQuery
     *            the compiled query
     * 
     * @since 2.0.0
     */
    public void addNamedQuery(String name, JpqlQuery jpqlQuery) {
        if (this.namedQueries.containsKey(name)) {
            throw new IllegalArgumentException("A named query with the same name already exists: " + name);
        }

        this.namedQueries.put(name, jpqlQuery);
    }

    /**
     * Adds the query to the named queries.
     * 
     * @param name
     *            the name fo the query
     * @param query
     *            the query
     * 
     * @since 2.0.0
     */
    public void addNamedQuery(final String name, Query query) {
        final QueryImpl<?> typedQuery = (QueryImpl<?>) query;
        final String jpql = typedQuery.getCriteriaQuery().getJpql();

        new JpqlQuery(this, this.criteriaBuilder, new NamedQueryMetadata() {

            @Override
            public Map<String, Object> getHints() {
                return typedQuery.getHints();
            }

            @Override
            public AbstractLocator getLocator() {
                return null;
            }

            @Override
            public LockModeType getLockMode() {
                return typedQuery.getLockMode();
            }

            @Override
            public String getName() {
                return name;
            }

            @Override
            public String getQuery() {
                return jpql;
            }
        });
    }

    /**
     * Checks if the entity manager factory is open.
     * 
     * @since 2.0.0
     */
    protected void assertOpen() {
        if (!this.open) {
            throw new IllegalStateException("EntityManagerFactory has been previously closed");
        }
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public void close() {
        this.assertOpen();

        this.metamodel.stopIdGenerators();

        final String dropOnClose = (String) this.getProperty(BJPASettings.DROP_ON_CLOSE);
        if ("true".equalsIgnoreCase(dropOnClose)) {
            this.metamodel.dropAllTables(this.dataSource);
        }

        this.dataSource.close();

        this.open = false;
    }

    private DataSourceProxy createDatasource(String persistanceUnitName, PersistenceParser parser) {
        SqlLoggingType sqlLogging;
        long slowSqlThreshold;
        int jdbcFetchSize;

        try {
            sqlLogging = this.getProperty(BJPASettings.SQL_LOGGING) != null ? //
                    SqlLoggingType.valueOf(
                            ((String) this.getProperty(BJPASettings.SQL_LOGGING)).toUpperCase(Locale.ENGLISH))
                    : //
                    SqlLoggingType.NONE;
        } catch (final Exception e) {
            throw new IllegalArgumentException("Illegal value " + this.getProperty(BJPASettings.SQL_LOGGING)
                    + " for " + BJPASettings.SQL_LOGGING);
        }

        try {
            slowSqlThreshold = this.getProperty(BJPASettings.SLOW_SQL_THRESHOLD) != null ? //
                    Long.valueOf((String) this.getProperty(BJPASettings.SLOW_SQL_THRESHOLD)) : //
                    BJPASettings.DEFAULT_SLOW_SQL_THRESHOLD;

        } catch (final Exception e) {
            throw new IllegalArgumentException("Illegal value " + this.getProperty(BJPASettings.SLOW_SQL_THRESHOLD)
                    + " for " + BJPASettings.SLOW_SQL_THRESHOLD);
        }

        try {
            jdbcFetchSize = this.getProperty(BJPASettings.FETCH_SIZE) != null ? //
                    Integer.valueOf((String) this.getProperty(BJPASettings.FETCH_SIZE)) : //
                    BJPASettings.DEFAULT_FETCH_SIZE;

        } catch (final Exception e) {
            throw new IllegalArgumentException("Illegal value " + this.getProperty(BJPASettings.FETCH_SIZE)
                    + " for " + BJPASettings.FETCH_SIZE);
        }

        if (this.getProperty(BJPASettings.DATASOURCE_POOL) != null) {
            final String poolClassName = (String) this.getProperty(BJPASettings.DATASOURCE_POOL);
            final String hintName = (String) this.getProperty(BJPASettings.DATASOURCE_NAME);
            try {
                final Object newInstance = this.classloader.loadClass(poolClassName).newInstance();
                if (newInstance instanceof AbstractDataSource) {
                    this.dataSourcePool = (AbstractDataSource) newInstance;
                } else {
                    throw new IllegalArgumentException("Illegal value "
                            + this.getProperty(BJPASettings.DATASOURCE_POOL) + " for "
                            + BJPASettings.DATASOURCE_POOL
                            + " Please provide a datasource pool implementation extending org.batoo.jpa.jdbc.AbstractDataSourcePool");
                }
            } catch (final Exception e) {
                throw new IllegalArgumentException(
                        "Class not found: " + this.getProperty(BJPASettings.DATASOURCE_POOL));
            } finally {
                this.dataSourcePool.open(persistanceUnitName, hintName);
            }
        }
        return this.createDatasourceProxy(parser, sqlLogging, slowSqlThreshold, jdbcFetchSize);
    }

    private DataSource createDatasource0(PersistenceParser parser) {
        try {
            // read the properties
            final String jdbcDriver = (String) this.getProperty(JPASettings.JDBC_DRIVER);
            final String jdbcUrl = (String) this.getProperty(JPASettings.JDBC_URL);
            final String jdbcUser = (String) this.getProperty(JPASettings.JDBC_USER);
            final String jdbcPassword = (String) this.getProperty(JPASettings.JDBC_PASSWORD);

            final Integer statementsCacheSize = this.getProperty(BJPASettings.STATEMENT_CACHE_SIZE) != null ? //
                    Integer.valueOf((String) this.getProperty(BJPASettings.STATEMENT_CACHE_SIZE)) : //
                    BJPASettings.DEFAULT_STATEMENT_CACHE_SIZE;

            final Integer minConnections = this.getProperty(BJPASettings.MIN_CONNECTIONS) != null ? //
                    Integer.valueOf((String) this.getProperty(BJPASettings.MIN_CONNECTIONS)) : //
                    BJPASettings.DEFAULT_MIN_CONNECTIONS;

            // create the datasource
            final BoneCPDataSource dataSource = new BoneCPDataSource();

            dataSource.setDriverClass(jdbcDriver);
            dataSource.setJdbcUrl(jdbcUrl);
            dataSource.setUsername(jdbcUser);
            dataSource.setPassword(jdbcPassword);

            dataSource.setStatementsCacheSize(statementsCacheSize);
            dataSource.setMinConnectionsPerPartition(minConnections);
            dataSource.setMaxConnectionsPerPartition(5);
            dataSource.setDisableConnectionTracking(true);

            // This is slow so always set it to 0
            dataSource.setReleaseHelperThreads(0);

            return dataSource;
        } catch (final Exception e) {
            throw new IllegalArgumentException("Illegal values for datasource settings!");
        }
    }

    private DataSourceProxy createDatasourceProxy(PersistenceParser parser, SqlLoggingType sqlLogging,
            long slowSqlThreshold, int jdbcFetchSize) {
        final boolean external = (parser.getJtaDataSource() != null) || (parser.getNonJtaDataSource() != null);
        if (parser.getJtaDataSource() != null) {
            return new DataSourceProxy(parser.getJtaDataSource(), external, sqlLogging, slowSqlThreshold,
                    jdbcFetchSize);
        }
        if (parser.getNonJtaDataSource() != null) {
            return new DataSourceProxy(parser.getNonJtaDataSource(), external, sqlLogging, slowSqlThreshold,
                    jdbcFetchSize);
        }

        if (this.dataSourcePool != null) {
            return new DataSourceProxy(this.dataSourcePool, external, sqlLogging, slowSqlThreshold, jdbcFetchSize);
        }
        return new DataSourceProxy(this.createDatasource0(parser), external, sqlLogging, slowSqlThreshold,
                jdbcFetchSize);
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public EntityManagerImpl createEntityManager() {
        this.assertOpen();

        return new EntityManagerImpl(this, this.metamodel, this.dataSource, Collections.<String, Object>emptyMap(),
                this.jdbcAdaptor);
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public EntityManager createEntityManager(Map<String, Object> map) {
        this.assertOpen();

        return new EntityManagerImpl(this, this.metamodel, this.dataSource, map, this.jdbcAdaptor);
    }

    /**
     * Creates the JDBC adaptor.
     * 
     * @return the JDBC Adaptor
     * 
     * @since 2.0.0
     */
    private JdbcAdaptor createJdbcAdaptor() {
        int insertBatchSize;
        try {
            insertBatchSize = this.getProperty(BJPASettings.INSERT_BATCH_SIZE) != null ? //
                    Integer.valueOf(((String) this.getProperty(BJPASettings.INSERT_BATCH_SIZE))) : //
                    BJPASettings.DEFAULT_INSERT_BATCH_SIZE;
        } catch (final Exception e) {
            throw new IllegalArgumentException("Illegal value " + this.getProperty(BJPASettings.INSERT_BATCH_SIZE)
                    + " for " + BJPASettings.INSERT_BATCH_SIZE);
        }

        int removeBatchSize;
        try {
            removeBatchSize = this.getProperty(BJPASettings.REMOVE_BATCH_SIZE) != null ? //
                    Integer.valueOf(((String) this.getProperty(BJPASettings.REMOVE_BATCH_SIZE))) : //
                    BJPASettings.DEFAULT_REMOVE_BATCH_SIZE;
        } catch (final Exception e) {
            throw new IllegalArgumentException("Illegal value " + this.getProperty(BJPASettings.REMOVE_BATCH_SIZE)
                    + " for " + BJPASettings.REMOVE_BATCH_SIZE);
        }

        try {
            final Connection connection = this.dataSource.getConnection();
            try {
                final JdbcAdaptor adapter = AbstractJdbcAdaptor.getAdapter(this.classloader,
                        connection.getMetaData().getDatabaseProductName());

                adapter.setInsertBatchSize(insertBatchSize);
                adapter.setRemoveBatchSize(removeBatchSize);

                return adapter;
            } finally {
                connection.close();
            }
        } catch (final SQLException e) {
            throw new BatooException("Unable to get connection from the datasource", e);
        }
    }

    private ValidatorFactory createValidationFactory() {
        try {
            return Validation.buildDefaultValidatorFactory();
        } catch (final ValidationException e) {
            final ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();

            Thread.currentThread().setContextClassLoader(this.classloader);
            try {
                return Validation.buildDefaultValidatorFactory();
            } finally {
                Thread.currentThread().setContextClassLoader(oldClassLoader);
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public Cache getCache() {
        return null;
    }

    /**
     * Returns the classloader of the entity manager factory.
     * 
     * @return the classloader of the entity manager factory
     * 
     * @since 2.0.0
     */
    public ClassLoader getClassloader() {
        return this.classloader;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public CriteriaBuilderImpl getCriteriaBuilder() {
        this.assertOpen();

        return this.criteriaBuilder;
    }

    /**
     * Returns the datasource of the entity manager factory.
     * 
     * @return the datasource of the entity manager
     * 
     * @since 2.0.0
     */
    protected DataSource getDatasource() {
        return this.dataSource;
    }

    /**
     * Returns the JDBC Adaptor of the entity manager factory.
     * 
     * @return the JDBC Adaptor of the entity manager
     * 
     * @since 2.0.0
     */
    protected JdbcAdaptor getJdbcAdaptor() {
        return this.jdbcAdaptor;
    }

    /**
     * Returns a lazy created {@link JpqlQuery} for the query.
     * 
     * @param qlString
     *            the JPQL query string
     * @return the Jpql Query object
     * 
     * @since 2.0.0
     */
    public JpqlQuery getJpqlQuery(String qlString) {
        try {
            EntityManagerFactoryImpl.LOG.debug("JPQL: {0}", qlString);

            JpqlQuery jpqlQuery = this.jpqlCache.get(qlString);
            if (jpqlQuery == null) {
                jpqlQuery = new JpqlQuery(EntityManagerFactoryImpl.this, qlString);

                // clean up job
                if (this.jpqlCache.size() == EntityManagerFactoryImpl.NO_QUERIES_MAX) {
                    synchronized (this) {
                        if (this.jpqlCache.size() == EntityManagerFactoryImpl.NO_QUERIES_MAX) {
                            final JpqlQuery[] queries = Lists.newArrayList(this.jpqlCache.values())
                                    .toArray(new JpqlQuery[this.jpqlCache.size()]);
                            Arrays.sort(queries, new Comparator<JpqlQuery>() {

                                @Override
                                public int compare(JpqlQuery o1, JpqlQuery o2) {
                                    if (o1.getLastUsed() > o2.getLastUsed()) {
                                        return 1;
                                    }

                                    return -1;
                                }
                            });

                            for (int i = 0; i < EntityManagerFactoryImpl.NO_QUERIES_TRIM; i++) {
                                this.jpqlCache.remove(queries[i].getQueryString());
                            }
                        }
                    }
                }

                this.jpqlCache.put(qlString, jpqlQuery);
            }
            return jpqlQuery;
        } catch (final Exception e) {
            if (e.getCause() instanceof PersistenceException) {
                throw (PersistenceException) e.getCause();
            }

            if (e.getCause() instanceof IllegalArgumentException) {
                throw (IllegalArgumentException) e.getCause();
            }

            throw new PersistenceException("Cannot parse query: " + qlString, e);
        }
    }

    /**
     * Returns the global max fetch join depth.
     * 
     * @return the global max fetch join depth
     * 
     * @since 2.0.0
     */
    public int getMaxFetchJoinDepth() {
        return this.maxFetchJoinDepth;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public MetamodelImpl getMetamodel() {
        return this.metamodel;
    }

    /**
     * Returns the named query with the name <code>name</code>.
     * 
     * @param name
     *            the name of the query
     * @return the named query
     * 
     * @since 2.0.0
     */
    public JpqlQuery getNamedQuery(String name) {
        return this.namedQueries.get(name);
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public PersistenceUnitUtil getPersistenceUnitUtil() {
        return this.persistenceUtil;
    }

    /**
     * Returns the set of persist validators.
     * 
     * @return the set of persist validators.
     * 
     * @since 2.0.0
     */
    public Class<?>[] getPersistValidators() {
        return this.persistValidators;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public Map<String, Object> getProperties() {
        return this.properties;
    }

    /**
     * Returns the persistence property.
     * 
     * @param key
     *            the key for the property
     * @return the value of the property or null
     * 
     * @since 2.0.0
     */
    public Object getProperty(String key) {
        return this.properties.get(key);
    }

    /**
     * Returns the set of remove validators.
     * 
     * @return the set of remove validators.
     * 
     * @since 2.0.0
     */
    public Class<?>[] getRemoveValidators() {
        return this.removeValidators;
    }

    /**
     * Returns the set of update validators.
     * 
     * @return the set of update validators.
     * 
     * @since 2.0.0
     */
    public Class<?>[] getUpdateValidators() {
        return this.updateValidators;
    }

    /**
     * Returns the validation factory.
     * 
     * @return the validation factory.
     * 
     * @since 2.0.0
     */
    public ValidatorFactory getValidationFactory() {
        return this.validationFactory;
    }

    private Class<?>[] getValidatorsFor(PersistenceParser parser, String group) {

        final String groups = (String) parser.getProperties().get(group);
        if (StringUtils.isBlank(groups)) {
            return new Class[] { Default.class };
        }

        final Set<Class<?>> validationGroups = Sets.newHashSet();
        final Iterator<String> i = Splitter.on(",").trimResults().split(groups).iterator();
        while (i.hasNext()) {
            final String className = i.next();
            try {
                validationGroups.add(this.classloader.loadClass(className));
            } catch (final ClassNotFoundException e) {
                throw new PersistenceException("Cannot load class for validation group: " + className);
            }
        }

        return validationGroups.toArray(new Class[validationGroups.size()]);
    }

    /**
     * Returns if the persistence unit has validators
     * 
     * @return true if the persistence unit has validators, false otherwise
     * 
     * @since 2.0.0
     */
    public boolean hasValidators() {
        return this.validationFactory != null;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public boolean isOpen() {
        return this.open;
    }

    private void prepareProperties(PersistenceParser parser) {
        final Enumeration<?> e = System.getProperties().propertyNames();

        while (e.hasMoreElements()) {
            final Object key = e.nextElement();
            if (key instanceof String) {
                this.properties.put((String) key, System.getProperties().get(key));
            }
        }
        this.properties.putAll(parser.getProperties());

        //load runtime-properties, version, build etc
        this.properties.putAll(BatooUtils.loadRuntimeProperties());
    }

    private DDLMode readDdlMode() {
        final String ddlMode = (String) this.getProperty(BJPASettings.DDL);

        if (ddlMode == null) {
            return DDLMode.NONE;
        }

        return DDLMode.valueOf(ddlMode.toUpperCase());
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> T unwrap(Class<T> clazz) {
        if (clazz == EntityManagerFactoryImpl.class) {
            return (T) this;
        }

        if ((clazz == BoneCPDataSource.class) && (this.dataSource.getDelegate() instanceof BoneCPDataSource)) {
            return (T) this.dataSource.getDelegate();
        }

        if ((clazz == BoneCP.class) && (this.dataSource.getDelegate() instanceof BoneCP)) {
            return (T) this.dataSource.getDelegate();
        }

        return null;
    }
}