From 744bab0213dd3c951c62c962198ed8a53aad5339 Mon Sep 17 00:00:00 2001 From: Arseni Bulatski Date: Thu, 15 Feb 2018 13:10:05 +0300 Subject: [PATCH 1/2] CAY-2402 add validation --- RELEASE-NOTES.txt | 1 + .../cayenne/gen/ClassGenerationAction.java | 4 ++-- .../org/apache/cayenne/gen/StringUtils.java | 20 ++++++++++++++++--- .../validation/ObjAttributeValidator.java | 7 +++++++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index bcbd440d81..9dbacc7ff1 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -21,6 +21,7 @@ CAY-2393 Add sqlserver-docker profile to automate tests on SQLServer CAY-2394 Upgrade to Apache Velocity 2.0 CAY-2395 cdbimport: add option to create project file CAY-2396 Upgrade maven-assembly-plugin to 3.1.0 +CAY-2402 cdbimport: escape db names that is not valid Java identifiers CAY-2403 Extract eventbridges to top level CAY-2404 Move itests to maven-plugins diff --git a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java index 253ca063d2..a0f1c94270 100644 --- a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java +++ b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java @@ -161,13 +161,13 @@ protected void resetContextForArtifact(Artifact artifact) { String qualifiedClassName = artifact.getQualifiedClassName(); String packageName = stringUtils.stripClass(qualifiedClassName); - String className = stringUtils.stripPackageName(qualifiedClassName); + String className = stringUtils.addUnderscoreIfDigitFirst(stringUtils.stripPackageName(qualifiedClassName)); String qualifiedBaseClassName = artifact.getQualifiedBaseClassName(); String basePackageName = stringUtils.stripClass(qualifiedBaseClassName); String baseClassName = stringUtils.stripPackageName(qualifiedBaseClassName); - String superClassName = getSuperclassPrefix() + stringUtils.stripPackageName(qualifiedClassName); + String superClassName = getSuperclassPrefix() + className; String superPackageName = this.superPkg; if (superPackageName == null) { diff --git a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java index c48fd23c8e..3a87c8bdec 100644 --- a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java +++ b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/StringUtils.java @@ -39,9 +39,10 @@ public static StringUtils getInstance() { /** * Prepends underscore to variable name if necessary to remove conflict with reserved - * keywords. + * keywords and variable name begins with digit. */ public String formatVariableName(String variableName) { + variableName = addUnderscoreIfDigitFirst(variableName); if (NameValidationHelper.getInstance().isReservedJavaKeyword(variableName)) { return "_" + variableName; } @@ -50,6 +51,19 @@ public String formatVariableName(String variableName) { } } + /** + * Add underscore to variable name if first letter + * is digit. + * + * @since 4.1 + */ + public String addUnderscoreIfDigitFirst(String variableName) { + if(Character.isDigit(variableName.charAt(0))){ + return "_" + variableName; + } else { + return variableName; + } + } /** * Removes package name, leaving base name. * @@ -109,12 +123,13 @@ public String capitalizedAsConstant(String name) { // clear of non-java chars. While the method name implies that a passed identifier // is pure Java, it is used to build pk columns names and such, so extra safety // check is a good idea - name = Util.specialCharsToJava(name); + name = addUnderscoreIfDigitFirst(Util.specialCharsToJava(name)); char charArray[] = name.toCharArray(); StringBuilder buffer = new StringBuilder(); for (int i = 0; i < charArray.length; i++) { + if ((Character.isUpperCase(charArray[i])) && (i != 0)) { char prevChar = charArray[i - 1]; @@ -125,7 +140,6 @@ public String capitalizedAsConstant(String name) { buffer.append(Character.toUpperCase(charArray[i])); } - return buffer.toString(); } diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/validation/ObjAttributeValidator.java b/cayenne-project/src/main/java/org/apache/cayenne/project/validation/ObjAttributeValidator.java index 1cd3d10df1..b450521472 100644 --- a/cayenne-project/src/main/java/org/apache/cayenne/project/validation/ObjAttributeValidator.java +++ b/cayenne-project/src/main/java/org/apache/cayenne/project/validation/ObjAttributeValidator.java @@ -60,6 +60,13 @@ private void validateName(ObjAttribute attribute, ValidationResult validationRes return; } + // Must contain character + if(!attribute.getName().matches(".*[a-z].*")){ + addFailure(validationResult, attribute, "ObjAttribute name '%s' must contain at least one character.", + attribute.getName()); + return; + } + NameValidationHelper helper = NameValidationHelper.getInstance(); String invalidChars = helper.invalidCharsInObjPathComponent(attribute.getName()); From 0feef03afe360ff27e6deae1bdba5dc21b5fef47 Mon Sep 17 00:00:00 2001 From: Arseni Bulatski Date: Fri, 16 Feb 2018 11:25:12 +0300 Subject: [PATCH 2/2] Add dataMap validation to dbImportAction --- .../reverse/dbimport/DbImportModule.java | 3 +++ .../dbimport/DefaultDbImportAction.java | 23 ++++++++++++++++++- .../dbimport/DefaultDbImportActionTest.java | 8 ++++++- .../dialog/db/load/ModelerDbImportAction.java | 6 +++-- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportModule.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportModule.java index 7cb227c21c..e5e5744d48 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportModule.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportModule.java @@ -33,6 +33,8 @@ import org.apache.cayenne.project.ProjectModule; import org.apache.cayenne.project.ProjectSaver; import org.apache.cayenne.project.extension.ExtensionAwareHandlerFactory; +import org.apache.cayenne.project.validation.DefaultProjectValidator; +import org.apache.cayenne.project.validation.ProjectValidator; /** * A DI module that bootstraps {@link DbImportAction}. @@ -46,6 +48,7 @@ public class DbImportModule implements Module { public void configure(Binder binder) { binder.bind(DbImportAction.class).to(DefaultDbImportAction.class); binder.bind(ProjectSaver.class).to(FileProjectSaver.class); + binder.bind(ProjectValidator.class).to(DefaultProjectValidator.class); binder.bind(ConfigurationNameMapper.class).to(DefaultConfigurationNameMapper.class); binder.bind(DataMapLoader.class).to(XMLDataMapLoader.class); binder.bind(HandlerFactory.class).to(DefaultHandlerFactory.class); diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java index 37d895ee5b..0315d169da 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java @@ -28,10 +28,12 @@ import org.apache.cayenne.configuration.server.DataSourceFactory; import org.apache.cayenne.configuration.server.DbAdapterFactory; import org.apache.cayenne.dba.DbAdapter; +import org.apache.cayenne.dbsync.DbSyncModule; import org.apache.cayenne.dbsync.merge.token.model.AbstractToModelToken; import org.apache.cayenne.dbsync.merge.DataMapMerger; import org.apache.cayenne.dbsync.merge.context.MergerContext; import org.apache.cayenne.dbsync.merge.token.MergerToken; +import org.apache.cayenne.dbsync.reverse.configuration.ToolsModule; import org.apache.cayenne.dbsync.reverse.dbload.ModelMergeDelegate; import org.apache.cayenne.dbsync.reverse.dbload.ProxyModelMergeDelegate; import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory; @@ -42,7 +44,9 @@ import org.apache.cayenne.dbsync.reverse.filters.CatalogFilter; import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig; import org.apache.cayenne.dbsync.reverse.filters.PatternFilter; +import org.apache.cayenne.di.DIBootstrap; import org.apache.cayenne.di.Inject; +import org.apache.cayenne.di.Injector; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.EntityResolver; @@ -50,7 +54,9 @@ import org.apache.cayenne.map.ObjRelationship; import org.apache.cayenne.map.Procedure; import org.apache.cayenne.project.Project; +import org.apache.cayenne.project.ProjectModule; import org.apache.cayenne.project.ProjectSaver; +import org.apache.cayenne.project.validation.ProjectValidator; import org.apache.cayenne.resource.URLResource; import org.apache.cayenne.validation.SimpleValidationFailure; import org.apache.cayenne.validation.ValidationFailure; @@ -84,6 +90,7 @@ public class DefaultDbImportAction implements DbImportAction { private final DataMapLoader mapLoader; private final MergerTokenFactoryProvider mergerTokenFactoryProvider; private final DataChannelDescriptorLoader dataChannelDescriptorLoader; + private final ProjectValidator projectValidator; public DefaultDbImportAction(@Inject Logger logger, @Inject ProjectSaver projectSaver, @@ -91,7 +98,8 @@ public DefaultDbImportAction(@Inject Logger logger, @Inject DbAdapterFactory adapterFactory, @Inject DataMapLoader mapLoader, @Inject MergerTokenFactoryProvider mergerTokenFactoryProvider, - @Inject DataChannelDescriptorLoader dataChannelDescriptorLoader) { + @Inject DataChannelDescriptorLoader dataChannelDescriptorLoader, + @Inject ProjectValidator projectValidator) { this.logger = logger; this.projectSaver = projectSaver; this.dataSourceFactory = dataSourceFactory; @@ -99,6 +107,7 @@ public DefaultDbImportAction(@Inject Logger logger, this.mapLoader = mapLoader; this.mergerTokenFactoryProvider = mergerTokenFactoryProvider; this.dataChannelDescriptorLoader = dataChannelDescriptorLoader; + this.projectValidator = projectValidator; } protected static List sort(List reverse) { @@ -180,11 +189,23 @@ public void execute(DbImportConfiguration config) throws Exception { hasChanges |= applyTokens(targetDataMap, tokens, config); hasChanges |= syncProcedures(targetDataMap, sourceDataMap, loaderConfig.getFiltersConfig()); + validateDataMap(targetDataMap); + if (hasChanges) { saveLoaded(targetDataMap, config); } } + protected void validateDataMap(DataMap targetDataMap){ + ValidationResult validationResult = projectValidator.validate(targetDataMap); + + if(validationResult.hasFailures()){ + for(ValidationFailure validationFailure : validationResult.getFailures()) { + logger.warn(validationFailure.getDescription()); + } + } + } + protected void transformSourceBeforeMerge(DataMap sourceDataMap, DataMap targetDataMap, DbImportConfiguration configuration) { if (configuration.isForceDataMapCatalog()) { String catalog = targetDataMap.getDefaultCatalog(); diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java index 364f770e17..55d71104b1 100644 --- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java @@ -50,9 +50,11 @@ import org.apache.cayenne.project.FileProjectSaver; import org.apache.cayenne.project.Project; import org.apache.cayenne.project.extension.ProjectExtension; +import org.apache.cayenne.project.validation.ProjectValidator; import org.apache.cayenne.resource.Resource; import org.apache.cayenne.resource.URLResource; import org.apache.cayenne.util.Util; +import org.apache.cayenne.validation.ValidationResult; import org.slf4j.Logger; import org.junit.Before; import org.junit.Test; @@ -303,7 +305,11 @@ private DefaultDbImportAction buildDbImportAction(FileProjectSaver projectSaver, DataChannelDescriptorLoader dataChannelDescriptorLoader = mock(DataChannelDescriptorLoader.class); - return new DefaultDbImportAction(log, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider, dataChannelDescriptorLoader) { + ProjectValidator projectValidator = mock(ProjectValidator.class); + + when(projectValidator.validate((DataMap)any())).thenReturn(new ValidationResult()); + + return new DefaultDbImportAction(log, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider, dataChannelDescriptorLoader, projectValidator) { protected DbLoader createDbLoader(DbAdapter adapter, Connection connection, diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java index b98313fef9..47efaf5417 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java @@ -28,6 +28,7 @@ import org.apache.cayenne.project.ProjectSaver; import org.apache.cayenne.dbsync.reverse.dbimport.DbImportConfiguration; import org.apache.cayenne.dbsync.reverse.dbimport.DefaultDbImportAction; +import org.apache.cayenne.project.validation.ProjectValidator; import org.slf4j.Logger; import java.io.IOException; @@ -43,8 +44,9 @@ public ModelerDbImportAction(@Inject Logger logger, @Inject DbAdapterFactory adapterFactory, @Inject DataMapLoader mapLoader, @Inject MergerTokenFactoryProvider mergerTokenFactoryProvider, - @Inject DataChannelDescriptorLoader dataChannelDescriptorLoader) { - super(logger, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider, dataChannelDescriptorLoader); + @Inject DataChannelDescriptorLoader dataChannelDescriptorLoader, + @Inject ProjectValidator projectValidator) { + super(logger, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider, dataChannelDescriptorLoader, projectValidator); } @Override