/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* MappingGenerator.java
*
* Created on Aug 18, 2003
*/
package com.sun.jdo.spi.persistence.support.ejb.ejbc;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import com.sun.ejb.codegen.EjbcContext;
import com.sun.ejb.codegen.GeneratorException;
import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.deployment.ResourceReferenceDescriptor;
import com.sun.enterprise.deployment.backend.DeploymentStatus;
import com.sun.enterprise.server.Constants;
import com.sun.jdo.api.persistence.mapping.ejb.beans.EntityMapping;
import com.sun.jdo.api.persistence.mapping.ejb.beans.CmpFieldMapping;
import com.sun.jdo.api.persistence.mapping.ejb.beans.CmrFieldMapping;
import com.sun.jdo.api.persistence.mapping.ejb.beans.ColumnPair;
import com.sun.jdo.api.persistence.mapping.ejb.beans.SunCmpMapping;
import com.sun.jdo.api.persistence.mapping.ejb.beans.SunCmpMappings;
import com.sun.jdo.api.persistence.mapping.ejb.ConversionHelper;
import com.sun.jdo.api.persistence.mapping.ejb.ConversionException;
import com.sun.jdo.api.persistence.mapping.ejb.MappingFile;
import com.sun.jdo.api.persistence.mapping.ejb.AbstractNameMapper;
import com.sun.jdo.api.persistence.model.Model;
import com.sun.jdo.api.persistence.model.ModelException;
import com.sun.jdo.api.persistence.model.mapping.MappingClassElement;
import com.sun.jdo.spi.persistence.utility.StringHelper;
import com.sun.jdo.spi.persistence.utility.I18NHelper;
import com.sun.jdo.spi.persistence.utility.logging.Logger;
import com.sun.jdo.spi.persistence.utility.database.DBVendorTypeHelper;
import com.sun.jdo.spi.persistence.utility.database.DatabaseConstants;
import com.sun.jdo.spi.persistence.support.sqlstore.ejb.DeploymentHelper;
import com.sun.jdo.spi.persistence.generator.database.DatabaseGenerator;
import org.netbeans.modules.dbschema.DBException;
import org.netbeans.modules.dbschema.DBIdentifier;
import org.netbeans.modules.dbschema.SchemaElement;
import org.netbeans.modules.dbschema.jdbcimpl.ConnectionProvider;
import org.netbeans.modules.dbschema.jdbcimpl.SchemaElementImpl;
import org.netbeans.modules.dbschema.util.NameUtil;
import org.netbeans.modules.schema2beans.Schema2BeansException;
import org.netbeans.modules.schema2beans.ValidateException;
/*
* This class will generate mapping classes from sun-cmp-mappings.xml
* and dbschema if they are available in the jar, or it will generate mapping
* classes based on ejb-jar.xml, bean classes and policy by invoking the
* database generation backend.
*
* @author Jie Leng
*/
public class MappingGenerator extends
com.sun.jdo.api.persistence.mapping.ejb.MappingGenerator {
// XXX To be removed when all callers are switched to use
// DatabaseConstants.JAVA_TO_DB_FLAG directly.
public static final String JAVA_TO_DB_FLAG = DatabaseConstants.JAVA_TO_DB_FLAG;
private static final String DBSCHEMA_EXTENSION = ".dbschema"; // NOI18N
private static final char DOT = '.'; // NOI18N
/** The logger */
private static final Logger logger = LogHelperEJBCompiler.getLogger();
private final EjbBundleDescriptor bundle;
private String dbVendorName = null;
private boolean isJavaToDatabaseFlag = false;
private boolean isVerifyFlag = false;
/**
* I18N message handler
*/
private final static ResourceBundle messages = I18NHelper.loadBundle(
MappingGenerator.class);
/**
* Constructor
* @param bundle an ejb bundle
* @param model a model containing mapping class and
* persistence class information
* @param nameMapper a nameMapper for name lookup
* @param loader a class loader
*/
public MappingGenerator(EjbBundleDescriptor bundle, Model model,
NameMapper nameMapper, ClassLoader loader) {
super(new EJBBundleInfoHelper(bundle, nameMapper, model, null), loader, false);
this.bundle = bundle;
}
/**
* This method will load mapping classes if there is sun-cmp-mappings.xml,
* otherwise it will call the database generation backend to create
* mapping classes and schema. It also generates *.dbschema and
* sun-cmp-mappings.xml in application dir if it is
* in creating mapping classes mode.
* @param ejbcContext an object containing CLI options for
* the database generation backend
* @param inputFilesPath the directory where sun-cmp-mappings.xml is located
* @param generatedXmlsPath the directory where the generated files are located
@ @param classout the directory where the classes are located
* @param ignoreSunDeploymentDescriptors use java2db generation if set to <code>true</code>.
* @return a SchemaElement for mapping classes mapped to
* @throws IOException
* @throws DBException
* @throws ModelException
* @throws Schema2BeansException
* @throws SQLException
* @throws GeneratorException
* @throws ConversionException
*/
public SchemaElement generateMapping(
EjbcContext ejbcContext, String inputFilesPath,
String generatedXmlsPath, File classout,
boolean ignoreSunDeploymentDescriptors)
throws IOException, DBException, ModelException,
Schema2BeansException, SQLException, GeneratorException,
ConversionException {
SchemaElement schema = null;
if (ejbcContext == null)
isVerifyFlag = true;
File cmpMappingFile = getSunCmpMappingFile(inputFilesPath);
boolean mappedBeans = !ignoreSunDeploymentDescriptors
&& cmpMappingFile.exists();
ResourceReferenceDescriptor cmpResource = checkOrCreateCMPResource(
mappedBeans);
// Remember whether or not this mapping was created by Java2DB.
isJavaToDatabaseFlag = DeploymentHelper.isJavaToDatabase(
cmpResource.getSchemaGeneratorProperties());
// We *must* get a vendor name if either the beans are not mapped, or
// they are mapped and the javaToDatabase flag is set.
boolean mustHaveDBVendorName =
!mappedBeans || (mappedBeans && isJavaToDatabaseFlag);
// Read deployment settings from the deployment descriptor
// and CLI options.
Results deploymentArguments = getDeploymentArguments(
ejbcContext, cmpResource, mustHaveDBVendorName);
dbVendorName = deploymentArguments.getDatabaseVendorName();
if (mappedBeans) {
// If sun-cmp-mappings.xml exists and we are doing a deployment,
// validate some arguments and make sure we have dbschema.
// If it is from verify, skip deployment arguments check.
if (!isVerifyFlag) {
String warning = null; // Warning for user, if required.
if (isJavaToDatabaseFlag) {
// If beans were already mapped, we will generate tables, but
// they will be as per the existing mapping. So if the user
// gave --uniquetablenames, warn them that we will not take
// that flag into account. I.e., the tables will be generated
// as per the mapping.
if (deploymentArguments.hasUniqueTableNames()) {
warning =
I18NHelper.getMessage(
messages,
"EXC_DisallowJava2DBUniqueTableNames", //NOI18N
bundle.getApplication().getRegistrationName(),
JDOCodeGeneratorHelper.getModuleName(bundle));
logger.warning(warning);
}
} else if (deploymentArguments.hasJavaToDatabaseArgs()) {
// If beans are already mapped but the user gave any Java2DB
// command line arguments, warn the user that these args
// should not be used when module is already mapped.
warning =
I18NHelper.getMessage(
messages,
"EXC_DisallowJava2DBCLIOverrides", //NOI18N
bundle.getApplication().getRegistrationName(),
JDOCodeGeneratorHelper.getModuleName(bundle));
logger.warning(warning);
}
if (warning != null) {
DeploymentStatus status =
ejbcContext.getDeploymentRequest()
.getCurrentDeploymentStatus();
status.setStageStatus(DeploymentStatus.WARNING);
String msg = status.getStageStatusMessage();
msg = (msg == null) ? warning : (msg + "\n" + warning); // NOI18N
status.setStageStatusMessage(msg);
}
}
// Sun-cmp-mapping.xml exists, use normal MappingClass loading
SunCmpMappings sunCmpMappings = getSunCmpMappings(cmpMappingFile);
// Ensure that there is a dbschema for each element of
// sunCmpMappings.
ensureDBSchemaExistence(cmpResource, sunCmpMappings, inputFilesPath,
classout);
// load real mapping model and jdo model in memory
Map mappingClasses = loadMappingClasses(sunCmpMappings, getClassLoader());
// Get schema from one of the mapping classes.
// The mapping class element may be null if there is inconsistency
// in sun-cmp-mappings.xml and ejb-jar.xml. For example,
// the bean has mapping information in sun-cmp-mappings.xml but
// no definition in the ejb-jar.xml.
// So iterate over the mappings until the 1st non-null is found.
MappingClassElement mc = null;
Iterator iter = mappingClasses.values().iterator();
while (iter.hasNext()) {
mc = (MappingClassElement)iter.next();
if (mc != null) {
schema = SchemaElement.forName(mc.getDatabaseRoot());
break;
}
}
if (logger.isLoggable(Logger.FINE)){
logger.fine("Loaded mapped beans for " // NOI18N
+ cmpResource.getJndiName()
+ ", isJavaToDatabase=" + isJavaToDatabaseFlag); // NOI18N
}
}
else {
// Generate mapping file and dbschema, since either
// sun-cmp-mappings.xml does not exist (e.g. user didn't yet map)
// or ejbcContext is null (e.g. running under auspices of AVK).
DatabaseGenerator.Results results = generateMappingClasses(
dbVendorName, deploymentArguments.getUseUniqueTableNames(),
deploymentArguments.getUserPolicy(), inputFilesPath);
// java2db from verifier should not save anything to disk
if (!isVerifyFlag) {
// save SunCmpMapping to sun-cmp-mappings.xml
// in generated XML dir
writeSunCmpMappingFile(results.getMappingClasses(),
getSunCmpMappingFile(generatedXmlsPath));
schema = results.getSchema();
// save schema to dbschema file in generated XML dir
writeSchemaFile(schema, classout);
setJavaToDatabase(cmpResource, true);
}
}
return schema;
}
public String getDatabaseVendorName() {
return dbVendorName;
}
/**
* Returns javatodb flag in cmpResource.
* @return true if there is name as "javatodb" and value as "true"
*/
public boolean isJavaToDatabase() {
return isJavaToDatabaseFlag;
}
/**
* Set javatodb flag into SchemaGeneratorProperties
* @param cmpResource a ResourceReferenceDescriptor
* @param value a string containing true or false
*/
private void setJavaToDatabase(ResourceReferenceDescriptor
cmpResource, boolean value) {
if (logger.isLoggable(Logger.FINE)) {
logger.fine("set javatodb flag to " + value + " in cmpResource"); // NOI18N
}
Properties schemaGeneratorProperties = cmpResource.
getSchemaGeneratorProperties();
if (schemaGeneratorProperties == null) {
schemaGeneratorProperties = new Properties();
cmpResource.setSchemaGeneratorProperties(schemaGeneratorProperties);
}
schemaGeneratorProperties.setProperty(DatabaseConstants.JAVA_TO_DB_FLAG,
String.valueOf(value));
isJavaToDatabaseFlag = value;
}
/**
* Loads sun-cmp-mapping.xml into memory as SunCmpMappings
* @param cmpMappingFile a file of sun-cmp-mappings.xml
* @return a SunCmpMappings object
* @throws IOException
* @throws Schema2BeansException
*/
private SunCmpMappings getSunCmpMappings(File cmpMappingFile)
throws IOException, Schema2BeansException, GeneratorException {
InputStream is = null;
BufferedInputStream iasMapping = null;
SunCmpMappings sunCmpMapping = null;
if (cmpMappingFile.length() == 0) {
throw JDOCodeGeneratorHelper.createGeneratorException(
"CMG.BeansFileSizeIsZero", bundle); // NOI18N
}
try {
is = new FileInputStream(cmpMappingFile);
iasMapping = new BufferedInputStream(is);
sunCmpMapping = SunCmpMappings.createGraph(iasMapping);
} catch (IOException ex) {
throw ex;
} finally {
if (is != null) {
try {
is.close();
} catch(Exception ex) {
if (logger.isLoggable(Logger.FINE))
logger.fine(ex.toString());
}
}
if (iasMapping != null) {
try {
iasMapping.close();
} catch(Exception ex) {
if (logger.isLoggable(Logger.FINE))
logger.fine(ex.toString());
}
}
}
try {
sunCmpMapping.validate();
} catch (ValidateException ex) {
throw JDOCodeGeneratorHelper.createGeneratorException(
"CMG.InvalidSunCmpMappingsFile", bundle, ex); // NOI18N
}
return sunCmpMapping;
}
/**
* Gets sun-cmp-mappings.xml file
* @param filesPath a string consisting file path
* @return a file of sun-cmp-mappings.xml
*/
private static File getSunCmpMappingFile(String filesPath) {
String cmpMappingFile = (new StringBuffer(filesPath).
append(File.separator).
append(MappingFile.DEFAULT_LOCATION_IN_EJB_JAR)).toString();
// if the file contains directory structure, we need
// to create those directories if they do not exist.
if (cmpMappingFile.lastIndexOf(File.separatorChar) != -1) {
String dirs = cmpMappingFile.substring(
0, cmpMappingFile.lastIndexOf(File.separatorChar));
File fileDirs = new File(dirs);
if (!fileDirs.exists())
fileDirs.mkdirs();
}
return new File(cmpMappingFile);
}
/**
* Writes to sun-cmp-mappings.xml from mappings classes
* @param mappingClasses a set of mapping classes
* @param cmpMappingFile corresponds to sun-cmp-mappings.xml
* @throws IOException
* @throws ConversionException
* @throws Schema2BeansException
*/
private void writeSunCmpMappingFile(Set mappingClasses, File cmpMappingFile)
throws IOException, ConversionException, Schema2BeansException {
// Construct the input to MappingFile.fromMappingClasses(): a Map
// object containing ejbName and MappingClassElement. Use the
// elements of iteration and NameMapper to create the input for
// MappingFile.
Map mappingMap = new HashMap();
AbstractNameMapper nameMapper = getNameMapper();
Iterator iter = mappingClasses.iterator();
while (iter.hasNext()) {
MappingClassElement mappingClass = (MappingClassElement)iter.next();
String ejbName = nameMapper.getEjbNameForPersistenceClass(
mappingClass.getName());
mappingMap.put(ejbName, mappingClass);
}
MappingFile mf = new MappingFile();
OutputStream sunCmpMapping = null;
try {
sunCmpMapping = new FileOutputStream(
cmpMappingFile);
mf.fromMappingClasses(sunCmpMapping, mappingMap,
getConversionHelper());
} catch (IOException ex) {
throw ex;
} finally {
try {
if (sunCmpMapping != null) {
sunCmpMapping.close();
}
} catch (IOException ex) {
if (logger.isLoggable(Logger.FINE))
logger.fine(ex.toString());
}
}
}
/**
* Writes to *.dbschema file from schema
* @param schema a SchemaElement
* @param filePath a directory where *.dbschema is located
* @throws IOException
*/
private static void writeSchemaFile(SchemaElement schema, File filePath)
throws IOException {
OutputStream schemaStream = null;
try {
schemaStream = new FileOutputStream(
new File(filePath, NameUtil.getSchemaResourceName(
schema.getName().getName())));
schema.save(schemaStream);
} catch (IOException ex) {
throw ex;
} finally {
try {
if (schemaStream != null) {
schemaStream.close();
}
} catch (IOException ex) {
if (logger.isLoggable(Logger.FINE))
logger.fine(ex.toString());
}
}
}
/**
* Returns <code>true</code> if the specified propertyValue represents
* a defined value, <code>false</code> otherwise. This implementation
* returns <code>true</code> if the value is not empty and does not equal
* Constants.UNDEFINED.
* @param propertyValue the value to be tested for defined
* @return <code>true</code> if the specified propertyValue represents
* a defined value, <code>false</code> otherwise
*/
protected boolean isPropertyDefined(String propertyValue) {
return (super.isPropertyDefined(propertyValue) &&
!Constants.UNDEFINED.equals(propertyValue));
}
/**
* Contains the results of getDeploymentArguments()
*/
private class Results {
private final String useUniqueTableNames;
private final String dbVendorName;
private final Properties userPolicy;
/**
* If true, then the user explicitly provided one or more command line
* args that are specific to Java2DB.
*/
private final boolean javaToDatabaseArgs;
Results(String useUniqueTableNames, String dbVendorName,
Properties userPolicy, boolean javaToDatabaseArgs) {
this.useUniqueTableNames = useUniqueTableNames;
this.dbVendorName = dbVendorName;
this.userPolicy = userPolicy;
this.javaToDatabaseArgs = javaToDatabaseArgs;
}
// XXX Get rid of getUseUniqueTableNames from all call sites that need a
// boolean (probably all of them), using hasUniqueTableNames instead
/** @return useUniqueTableNames */
public String getUseUniqueTableNames() {
return useUniqueTableNames;
}
/**
* @return true if --uniquetablenames was given on the command line.
*/
public boolean hasUniqueTableNames() {
return isPropertyDefined(useUniqueTableNames);
}
/**
* Returns true if any Java2DB arguments were given on the command
* line.
* @return javaToDatabaseArgs */
public boolean hasJavaToDatabaseArgs() {
return javaToDatabaseArgs;
}
/** @return dbVendorName */
public String getDatabaseVendorName() {
return dbVendorName;
}
/** @return userPolicy */
public Properties getUserPolicy() {
return userPolicy;
}
}
/** Reads deployment settings from the deployment descriptor and CLI options
* and populates the corresponding variables.
* @param ejbcContext CLI arguments are obtained from here.
* @param cmpResource Parameters from deployment descriptor are obtained
* from here.
* @param connectToDatabase If true, then connect to database to get
* database vendor name if not otherwise available.
*/
private Results getDeploymentArguments(
EjbcContext ejbcContext,
ResourceReferenceDescriptor cmpResource,
boolean connectToDatabase) {
String useUniqueTableNames = null;
String dbVendorName = null;
Properties userPolicy = null;
//Indicates that one or more Java2DB arguments were given on the command
//line.
boolean javaToDatabaseArgs = false;
// If ejbcContext is not available, then use what is specified by
// cmpResource.
if (null == ejbcContext) {
dbVendorName = cmpResource.getDatabaseVendorName();
} else {
// Otherwise, get the vendor name from one of the CLI overrides,
// cmpResource, or the actual database (in that order).
Properties cliOverrides = ejbcContext.getOptionalArguments();
useUniqueTableNames = cliOverrides.getProperty(
Constants.CMP_UNIQUE_TABLE_NAMES);
// In javaToDatabaseArgs, we collect whether or not we have seen
// any of the java to database - related arguments, starting with
// --uniquetablenames.
javaToDatabaseArgs =isPropertyDefined(useUniqueTableNames);
dbVendorName = cliOverrides.getProperty(Constants.CMP_DB_VENDOR_NAME);
javaToDatabaseArgs |= isPropertyDefined(dbVendorName);
// XXX This check can be removed when DeployCommand guarantees to
// not return UNDEFINED.
if (null == dbVendorName || dbVendorName.equals(Constants.UNDEFINED)) {
dbVendorName = cmpResource.getDatabaseVendorName();
}
// If there is no CLI override, and nothing specified in the
// cmp-resource, try to get the dbvendorname from the database.
if (null == dbVendorName && connectToDatabase) {
try {
Connection conn = DeploymentHelper.getConnection(
cmpResource.getJndiName());
dbVendorName = conn.getMetaData().getDatabaseProductName();
} catch (Exception ex) {
// Ignore exceptions and use default.
}
}
String createTables =
cliOverrides.getProperty(Constants.CMP_CREATE_TABLES);
javaToDatabaseArgs |= isPropertyDefined(createTables);
String dropAndCreateTables =
cliOverrides.getProperty(Constants.CMP_DROP_AND_CREATE_TABLES);
javaToDatabaseArgs |= isPropertyDefined(dropAndCreateTables);
}
if (null == dbVendorName) {
dbVendorName = DBVendorTypeHelper.DEFAULT_DB;
} else {
dbVendorName = DBVendorTypeHelper.getDBType(dbVendorName);
}
userPolicy = cmpResource.getSchemaGeneratorProperties();
return new Results(useUniqueTableNames, dbVendorName, userPolicy, javaToDatabaseArgs);
}
/**
* Check if cmp resource is specified in the deployment descriptor.
* If the beans are mapped (sun-cmp-mapping.xml is present), the cmp
* resource must be present, otherwise (in java2db case) we will create
* a default one. If it's java2db, we will also parse the CLI overrides,
* as the cmp resource provides the default values.
*
* @param mappedBeans true if beans are mapped in this module.
* @throws GeneratorException if beans are mapped but cmp resource is not
* specified.
*/
private ResourceReferenceDescriptor checkOrCreateCMPResource(
boolean mappedBeans)
throws GeneratorException {
ResourceReferenceDescriptor cmpResource =
bundle.getCMPResourceReference();
if (mappedBeans) {
if (cmpResource == null) {
// If mapping exists, the cmpResource must specify a
// database or a PMF JNDI name.
throw JDOCodeGeneratorHelper.createGeneratorException(
"EXC_MissingCMPResource", bundle); //NOI18N
}
} else {
if (cmpResource == null) {
// In JavaToDB case we can deploy to the default jdbc-resource.
cmpResource = new ResourceReferenceDescriptor();
cmpResource.setJndiName("jdbc/__default");
cmpResource.setDatabaseVendorName(DBVendorTypeHelper.DERBY);
cmpResource.setCreateTablesAtDeploy(true);
cmpResource.setDropTablesAtUndeploy(true);
bundle.setCMPResourceReference(cmpResource);
}
}
return cmpResource;
}
/**
* Check that there is a dbschema for each element of the SunCmpMappings.
* For those which are missing, create a corresponding .dbschema file.
* @param cmpResource Provides JNDI name for getting database connection
* @param sunCmpMappings SunCmpMappings which is checked for having schema
* @param inputFilesPath the directory where this bundle's files are located
* @param classout the directory where the classes are located
* @exception DBException Thrown if database model throws it
* @exception IOException Thrown if .dbschema file cannot be created.
* @exception SQLException Thrown if we cannot get get required info from
* the database.
*/
private void ensureDBSchemaExistence(
ResourceReferenceDescriptor cmpResource,
SunCmpMappings sunCmpMappings,
String inputFilesPath,
File classout)
throws DBException, SQLException, GeneratorException {
String generatedSchemaName = getInfoHelper().getSchemaNameToGenerate();
Set tables = new HashSet();
int size = sunCmpMappings.sizeSunCmpMapping();
// Sweep through the mappings to check dbschema existence. If a
// mapping does not have a dbschema, get a list of tables to be
// captured, then capture them, create the corresponding dbschema,
// and save it.
for (int i = 0; i < size; i++) {
SunCmpMapping sunCmpMapping = sunCmpMappings.getSunCmpMapping(i);
String schemaName = sunCmpMapping.getSchema();
if (StringHelper.isEmpty(schemaName)) {
if (!isVerifyFlag) {
// The tables in this section need to be captured.
addAllTables(sunCmpMapping, tables);
sunCmpMapping.setSchema(generatedSchemaName);
} else {
// If it is from verifier, capture schema internally
// to perform sun-cmp-mappings.xml and EJB validation
getConversionHelper().setEnsureValidation(false);
}
} else {
File dbschemaFile = new File(
new StringBuffer(inputFilesPath)
.append(File.separator)
.append(schemaName)
.append(DBSCHEMA_EXTENSION).toString());
if (! (dbschemaFile.exists()
&& dbschemaFile.isFile()
&& dbschemaFile.canRead())) {
throw new GeneratorException(
I18NHelper.getMessage(
messages, "CMG.MissingDBSchema", // NOI18N
bundle.getApplication().getRegistrationName(),
JDOCodeGeneratorHelper.getModuleName(bundle),
schemaName));
}
}
}
// If there were tables to be captured, they will be in the list.
// Now we need to go and capture those tables.
if (tables.size() > 0) {
String userSchema = null;
Connection con = DeploymentHelper.getConnection(cmpResource.getJndiName());
DatabaseMetaData dmd = con.getMetaData();
if (DBVendorTypeHelper.requireUpperCaseSchema(dmd)) {
userSchema = dmd.getUserName().trim().toUpperCase();
}
ConnectionProvider cp = new ConnectionProvider(con, dmd.getDriverName().trim());
if (userSchema != null) {
cp.setSchema(userSchema);
}
OutputStream outstream = null;
try {
SchemaElementImpl outSchemaImpl = new SchemaElementImpl(cp);
SchemaElement schemaElement = new SchemaElement(outSchemaImpl);
schemaElement.setName(DBIdentifier.create(generatedSchemaName));
if(dmd.getDatabaseProductName().compareToIgnoreCase("MYSQL") == 0)
outSchemaImpl.initTables(cp, new LinkedList(tables), new LinkedList(), true);
else
outSchemaImpl.initTables(cp, new LinkedList(tables), new LinkedList(), false);
outstream = new FileOutputStream(
new File(classout,
new StringBuffer(generatedSchemaName)
.append(DBSCHEMA_EXTENSION).toString()));
// XXX Unfortunately, if SchemaElement.save gets an
// IOException, it prints the stack trace but does not
// let us handle it :-(
schemaElement.save(outstream);
} catch (IOException ex) {
// Catch FileNotFound, etc.
throw JDOCodeGeneratorHelper.createGeneratorException(
"CMG.CannotSaveDBSchema", bundle, ex); // NOI18N
} finally {
cp.closeConnection();
try {
if (outstream != null) {
outstream.close();
}
} catch (IOException ex) {
if (logger.isLoggable(Logger.FINE))
logger.fine(ex.toString());
}
}
}
}
/**
* Adds all table names referenced by this <code>SunCmpMapping</code> element
* to this Set.
*
* @param sunCmpMapping the SunCmpMapping element to check.
* @param tables the Set to update.
*/
private void addAllTables(SunCmpMapping sunCmpMapping, Set tables) {
EntityMapping[] beans = sunCmpMapping.getEntityMapping();
for (int i = 0; i < beans.length; i++) {
// Always add the table name.
addTableName(beans[i].getTableName(), tables);
// Check if there are table names specified in the
// cmp-field-mapping.
CmpFieldMapping[] cmpfields = beans[i].getCmpFieldMapping();
for (int j = 0; j < cmpfields.length; j++) {
// There might be more than one column-name for each cmp field.
String[] names = cmpfields[j].getColumnName();
for (int jj = 0; jj < names.length; jj++) {
addRelatedTableName(names[jj], tables);
}
}
// Check the table names specified in the cmr-field-mapping.
CmrFieldMapping[] cmrfields = beans[i].getCmrFieldMapping();
for (int j = 0; j < cmrfields.length; j++) {
// There might be more than one column-pair for each cmr field.
ColumnPair[] pairs = cmrfields[j].getColumnPair();
for (int jj = 0; jj < pairs.length; jj++) {
String[] names = pairs[jj].getColumnName();
for (int jjj = 0; jjj < names.length; jjj++) {
addRelatedTableName(names[jjj], tables);
}
}
}
}
}
/**
* Add a valid (not null and not all spaces) table name to the
* Set of known table names.
*
* @param name the table name to add if it's a valid name.
* @param tables the Set to update.
*/
private void addTableName(String name, Set tables) {
if (!StringHelper.isEmpty(name)) {
if (logger.isLoggable(Logger.FINE)){
logger.fine("Adding Table to Capture Set: " + name); // NOI18N
}
tables.add(name);
}
}
/**
* Adds a table name, if it is specified as a part of the column name,
* to the Set of known table names.
*
* @param columnName the name of the column to use.
* @param tables the Set to update.
*/
private void addRelatedTableName(String columnName, Set tables) {
if (!StringHelper.isEmpty(columnName)) {
int l = columnName.indexOf(DOT);
if (l > 0) {
addTableName(columnName.substring(0, l), tables);
}
}
}
}
|