All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.persistence.jpa.schemageneration.EclipseLinkSchemaGenerationProcessor Maven / Gradle / Ivy

There is a newer version: 8.0.0-JDK17-M9
Show newest version
/*
 * Copyright (c) 2022 Contributors to the Eclipse Foundation
 * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.persistence.jpa.schemageneration;

import com.sun.enterprise.deployment.PersistenceUnitDescriptor;
import com.sun.logging.LogDomains;

import jakarta.persistence.spi.PersistenceUnitTransactionType;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.api.naming.SimpleJndiName;
import org.glassfish.persistence.common.DatabaseConstants;
import org.glassfish.persistence.common.Java2DBProcessorHelper;

import static org.glassfish.persistence.common.DatabaseConstants.CREATE_DDL_JDBC_FILE_SUFFIX;
import static org.glassfish.persistence.common.DatabaseConstants.DROP_DDL_JDBC_FILE_SUFFIX;

/**
 * SchemaGenerationProcessor that handles schema generation while running
 * against EclipseLink in pre JPA 2.1 mode For each persistence unit descriptors
 * that is defined for an application create the ddl scripts. Additionally if
 * the user has requested the tables to be created or dropped from the database
 * complete that action too.
 *
 * These are the principles and expectations of the implementation. We don't
 * want TopLink code to execute the DDLs, it should only generate them. So, we
 * always set the *generation-mode* to *script* in the PUInfo object before
 * passing it to createContainerEMF(). As a result TopLink never creates the
 * actual tables, nor does it drop them. The DDLs are executed by our code based
 * on user preference which considers inputs from persistence.xml and CLI. We
 * set the TopLink property to DROP_AND_CREATE in that map because we want it to
 * always generate both create- and dropDDL.jdbc files.
 *
 * @author pramodg
 */
public class EclipseLinkSchemaGenerationProcessor implements SchemaGenerationProcessor {

    // Defining the persistence provider class names here that we would use to
    // check if schema generation is supported.
    private static final String TOPLINK_PERSISTENCE_PROVIDER_CLASS_NAME_OLD = "oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider";
    private static final String TOPLINK_PERSISTENCE_PROVIDER_CLASS_NAME_NEW = "oracle.toplink.essentials.PersistenceProvider";
    private static final String ECLIPSELINK_PERSISTENCE_PROVIDER_CLASS_NAME = "org.eclipse.persistence.jpa.PersistenceProvider";

    // Constants for various property values.

    // Following constants are actually defined in
    // oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
    // and org.eclipse.persistence.jpa.config.PersistenceUnitProperties
    // This code assumes that the value of constant at both the place is same
    private static final String CREATE_ONLY = "create-tables"; // NOI18N
    private static final String DROP_AND_CREATE = "drop-and-create-tables"; // NOI18N
    private static final String NONE = "none"; // NOI18N

    private static final String DDL_BOTH_GENERATION = "both"; // NOI18N
    private static final String DDL_DATABASE_GENERATION = "database"; // NOI18N
    private static final String DDL_SQL_SCRIPT_GENERATION = "sql-script"; // NOI18N

    // property names for Toplink and EclipseLink
    private static final String TOPLINK_DDL_GENERATION = "toplink.ddl-generation";
    private static final String ECLIPSELINK_DDL_GENERATION = "eclipselink.ddl-generation";

    private static final String TOPLINK_DDL_GENERATION_OUTPUT_MODE = "toplink.ddl-generation.output-mode";
    private static final String ECLIPSELINK_DDL_GENERATION_OUTPUT_MODE = "eclipselink.ddl-generation.output-mode";

    private static final String TOPLINK_APP_LOCATION = "toplink.application-location";
    private static final String ECLIPSELINK_APP_LOCATION = "eclipselink.application-location";

    private static final String TOPLINK_CREATE_JDBC_DDL_FILE = "toplink.create-ddl-jdbc-file-name";
    private static final String ECLIPSELINK_CREATE_JDBC_DDL_FILE = "eclipselink.create-ddl-jdbc-file-name";

    private static final String TOPLINK_DROP_JDBC_DDL_FILE = "toplink.drop-ddl-jdbc-file-name";
    private static final String ECLIPSELINK_DROP_JDBC_DDL_FILE = "eclipselink.drop-ddl-jdbc-file-name";

    private static Logger logger = LogDomains.getLogger(EclipseLinkSchemaGenerationProcessor.class, LogDomains.PERSISTENCE_LOGGER);

    /**
     * Holds name of provider specific properties.
     */
    private ProviderPropertyNamesHolder providerPropertyNamesHolder;

    private Java2DBProcessorHelper helper;

    private Map overrides;

    private boolean isSchemaGenerationPU;

    /**
     * Creates a new instance of EclipseLinkSchemaGenerationProcessor using
     * Java2DBProcessorHelper
     */
    public EclipseLinkSchemaGenerationProcessor(String persistenceProviderClassName) {
        initializeProviderPropertyHolder(persistenceProviderClassName);
    }

    /**
     * @param pud The PersistenceUnitDescriptor for pu being deployed
     * @param context The deployment context
     */
    @Override
    public void init(PersistenceUnitDescriptor pud, DeploymentContext context) {
        this.helper = new Java2DBProcessorHelper(context);
        this.helper.init();

        String ddlGenerate = getPersistencePropVal(pud, providerPropertyNamesHolder.ddlGeneration, NONE);
        String ddlMode = getPersistencePropVal(pud, providerPropertyNamesHolder.ddlGenerationOutputMode, DDL_BOTH_GENERATION);

        // If CLI options are not set, use value from the the ddl-generate property
        // if defined in persistence.xml
        boolean userCreateTables = (ddlGenerate.equals(CREATE_ONLY) || ddlGenerate.equals(DROP_AND_CREATE)) && !ddlMode.equals(NONE);

        boolean createTables = helper.getCreateTables(userCreateTables);

        boolean userDropTables = ddlGenerate.equals(DROP_AND_CREATE)
                && (ddlMode.equals(DDL_DATABASE_GENERATION) || ddlMode.equals(DDL_BOTH_GENERATION));

        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Processing request with create tables: " + createTables // NOI18N
                    + ", drop tables: " + userDropTables); // NOI18N
        }

        if (createTables || userDropTables) {
            helper.setProcessorType("JPA", pud.getName());
            helper.setDropTablesValue(userDropTables, pud.getName());
            helper.setCreateTablesValue(userCreateTables && !ddlMode.equals(DDL_SQL_SCRIPT_GENERATION), pud.getName());

            // For a RESOURCE_LOCAL, managed pu, only non-jta-data-source should be
            // specified.
            SimpleJndiName dataSourceName = (PersistenceUnitTransactionType.JTA == PersistenceUnitTransactionType.valueOf(pud.getTransactionType()))
                    ? pud.getJtaDataSource()
                    : pud.getNonJtaDataSource();
            helper.setJndiName(dataSourceName, pud.getName());
            constructJdbcFileNames(pud);
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("Processing request to create files - create file: " + // NOI18N
                        helper.getCreateJdbcFileName(pud.getName()) + ", drop  file: " + // NOI18N
                        helper.getDropJdbcFileName(pud.getName()));
            }

            overrides = new HashMap<>();
            addSchemaGenerationPropertiesToOverrides(pud, overrides);
            isSchemaGenerationPU = true;
        }
    }

    @Override
    public Map getOverridesForSchemaGeneration() {
        return overrides;
    }

    @Override
    public Map getOverridesForSuppressingSchemaGeneration() {
        Map overridesForSuppressingSchemaGeneration = new HashMap<>();
        overridesForSuppressingSchemaGeneration.put(providerPropertyNamesHolder.ddlGenerationOutputMode, NONE);
        return overridesForSuppressingSchemaGeneration;
    }

    @Override
    public boolean isContainerDDLExecutionRequired() {
        // DDL execution is required if this is a schema generation pu
        return isSchemaGenerationPU;
    }

    private void initializeProviderPropertyHolder(String providerClassName) {
        // Initialize with EL names for each bundle to handle apps that have
        // multiple pus
        providerPropertyNamesHolder = new ProviderPropertyNamesHolder();

        // Override with TLE names if running against TLE
        if (TOPLINK_PERSISTENCE_PROVIDER_CLASS_NAME_NEW.equals(providerClassName)
                || TOPLINK_PERSISTENCE_PROVIDER_CLASS_NAME_OLD.equals(providerClassName)) {
            // for backward compatibility
            providerPropertyNamesHolder.appLocation = TOPLINK_APP_LOCATION;
            providerPropertyNamesHolder.createJdbcDdlFile = TOPLINK_CREATE_JDBC_DDL_FILE;
            providerPropertyNamesHolder.dropJdbcDdlFile = TOPLINK_DROP_JDBC_DDL_FILE;
            providerPropertyNamesHolder.ddlGeneration = TOPLINK_DDL_GENERATION;
            providerPropertyNamesHolder.ddlGenerationOutputMode = TOPLINK_DDL_GENERATION_OUTPUT_MODE;
        }
    }

    /**
     * Construct the name of the create and drop jdbc ddl files that would be
     * created. These name would be either obtained from the persistence.xml file
     * (if the user has defined them) or we would create default filenames
     *
     * @param parBundle the persistence unit descriptor that is being worked on.
     */
    private void constructJdbcFileNames(PersistenceUnitDescriptor parBundle) {
        String createJdbcFileName = getPersistencePropVal(parBundle, providerPropertyNamesHolder.createJdbcDdlFile, null);
        String dropJdbcFileName = getPersistencePropVal(parBundle, providerPropertyNamesHolder.dropJdbcDdlFile, null);

        if (createJdbcFileName != null && dropJdbcFileName != null) {
            return;
        }

        String filePrefix = Java2DBProcessorHelper.getDDLNamePrefix(parBundle.getParent().getParent());

        if (createJdbcFileName == null) {
            createJdbcFileName = filePrefix + DatabaseConstants.NAME_SEPARATOR + parBundle.getName()
                    + CREATE_DDL_JDBC_FILE_SUFFIX;
        }
        if (dropJdbcFileName == null) {
            dropJdbcFileName = filePrefix + DatabaseConstants.NAME_SEPARATOR + parBundle.getName()
                    + DROP_DDL_JDBC_FILE_SUFFIX;
        }

        helper.setCreateJdbcFileName(createJdbcFileName, parBundle.getName());
        helper.setDropJdbcFileName(dropJdbcFileName, parBundle.getName());
    }

    /**
     * This method is called after the jdbc files have been created. Iterate over
     * all created jdbc ddl files and execute it against the database to have the
     * required objects created.
     */
    @Override
    public void executeCreateDDL() {
        helper.createOrDropTablesInDB(true, "JPA");
    }

    private void addSchemaGenerationPropertiesToOverrides(PersistenceUnitDescriptor puDescriptor, Map overrides) {
        addPropertyToOverride(puDescriptor, overrides, providerPropertyNamesHolder.appLocation,
                helper.getGeneratedLocation(puDescriptor.getName()));
        addPropertyToOverride(puDescriptor, overrides, providerPropertyNamesHolder.createJdbcDdlFile,
                helper.getCreateJdbcFileName(puDescriptor.getName()));
        addPropertyToOverride(puDescriptor, overrides, providerPropertyNamesHolder.dropJdbcDdlFile,
                helper.getDropJdbcFileName(puDescriptor.getName()));

        // The puDescriptor might not have this property if schema generation is
        // triggered by deployment CLI override
        addPropertyToOverride(puDescriptor, overrides, providerPropertyNamesHolder.ddlGeneration, DROP_AND_CREATE);

        // If we are doing schema generation, we want DDL scripts to be generated
        addPropertyToOverride(puDescriptor, overrides, providerPropertyNamesHolder.ddlGenerationOutputMode, DDL_SQL_SCRIPT_GENERATION);

    }

    /**
     * Utility method that is used to actually set the property into the persistence
     * unit descriptor.
     *
     * @param descriptor the persistence unit descriptor that is being worked on.
     * @param propertyName the name of the property.
     * @param propertyValue the value of the property.
     */
    private static void addPropertyToOverride(PersistenceUnitDescriptor descriptor, Map overrides, String propertyName, String propertyValue) {
        String oldPropertyValue = descriptor.getProperties().getProperty(propertyName);
        if (oldPropertyValue == null) { // Do not override any value explicitly specified by the user
            overrides.put(propertyName, propertyValue);
        }
    }

    /**
     * Given a persistence unit descriptor return the value of a property if the
     * user has specified it. If the user has not defined this property return the
     * default value.
     *
     * @param parBundle the persistence unit descriptor that is being worked on.
     * @param propertyName the property name being checked.
     * @param defaultValue the default value to be used.
     * @return the property value.
     */
    private String getPersistencePropVal(PersistenceUnitDescriptor parBundle, String propertyName, String defaultValue) {
        return parBundle.getProperties().getProperty(propertyName, defaultValue);
    }

    /**
     * This processor only supports EclipseLink, the default persistence povider in
     * glassfish; or Toplink, the default provder for GF 2.x.
     *
     * @return true if persistence provider is EclipseLink or Toplink.
     */
    public static boolean isSupportedPersistenceProvider(final String providerClassName) {

        return providerClassName.equals(TOPLINK_PERSISTENCE_PROVIDER_CLASS_NAME_OLD)
                || providerClassName.equals(TOPLINK_PERSISTENCE_PROVIDER_CLASS_NAME_NEW)
                || providerClassName.equals(ECLIPSELINK_PERSISTENCE_PROVIDER_CLASS_NAME);
    }

    /**
     * Holds names of provider specific property
     */
    private static class ProviderPropertyNamesHolder {
        // Initialize property names with EL specific properties
        String appLocation = ECLIPSELINK_APP_LOCATION;
        String createJdbcDdlFile = ECLIPSELINK_CREATE_JDBC_DDL_FILE;
        String dropJdbcDdlFile = ECLIPSELINK_DROP_JDBC_DDL_FILE;
        String ddlGeneration = ECLIPSELINK_DDL_GENERATION;
        String ddlGenerationOutputMode = ECLIPSELINK_DDL_GENERATION_OUTPUT_MODE;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy