You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2020/05/21 14:53:53 UTC
[airavata] branch develop updated: AIRAVATA-3328 Call setters on
defaulted fields when mapping with Dozer
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/airavata.git
The following commit(s) were added to refs/heads/develop by this push:
new 4034c6a AIRAVATA-3328 Call setters on defaulted fields when mapping with Dozer
4034c6a is described below
commit 4034c6a5bed3d327ff2738468681164ee3a961eb
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu May 21 10:53:07 2020 -0400
AIRAVATA-3328 Call setters on defaulted fields when mapping with Dozer
---
.../registry/core/utils/CustomBeanFactory.java | 102 +++++++++++++++++++++
.../registry/core/utils/ObjectMapperSingleton.java | 2 +-
.../src/main/resources/dozer_mapping.xml | 5 +
.../registry/core/utils/CustomBeanFactoryTest.java | 54 +++++++++++
.../core/utils/ObjectMapperSingletonTest.java | 66 +++++++++++++
5 files changed, 228 insertions(+), 1 deletion(-)
diff --git a/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/utils/CustomBeanFactory.java b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/utils/CustomBeanFactory.java
new file mode 100644
index 0000000..153285a
--- /dev/null
+++ b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/utils/CustomBeanFactory.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.airavata.registry.core.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.thrift.TBase;
+import org.apache.thrift.TFieldIdEnum;
+import org.apache.thrift.TFieldRequirementType;
+import org.apache.thrift.meta_data.FieldMetaData;
+import org.dozer.BeanFactory;
+import org.dozer.util.MappingUtils;
+import org.dozer.util.ReflectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CustomBeanFactory implements BeanFactory {
+
+ private final static Logger logger = LoggerFactory.getLogger(CustomBeanFactory.class);
+
+ @Override
+ public Object createBean(Object source, Class<?> sourceClass, String targetBeanId) {
+ Object result;
+ Class<?> destClass = MappingUtils.loadClass(targetBeanId);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Creating bean of type " + destClass.getSimpleName());
+ }
+ result = ReflectionUtils.newInstance(destClass);
+ if (result instanceof TBase) {
+
+ callSettersOnThriftFieldsWithDefaults((TBase) result);
+ }
+ return result;
+ }
+
+ /**
+ * Thrift fields with default values aren't serialized and sent over the wire if
+ * their setters were never called. However, Dozer doesn't call the setter on
+ * the field of a target object when the target field's value already matches
+ * the source's field value. This results in the Thrift data model object field
+ * having the default value but it doesn't get serialized with that value (and
+ * for required fields validation fails). The following changes the semantics of
+ * defaulted Thrift fields a bit so that they are always "set" even if the
+ * source object had no such field, but this matches the more general semantics
+ * of what is expected from fields that have default values and it works around
+ * an annoyance with required default fields that would fail validation
+ * otherwise.
+ *
+ * <p>
+ * See AIRAVATA-3268 and AIRAVATA-3328 for more information.
+ *
+ * @param <T>
+ * @param <F>
+ * @param instance
+ */
+ private <T extends TBase<T, F>, F extends TFieldIdEnum> void callSettersOnThriftFieldsWithDefaults(
+ TBase<T, F> instance) {
+
+ try {
+ Field metaDataMapField = instance.getClass().getField("metaDataMap");
+ Map<F, FieldMetaData> metaDataMap = (Map<F, FieldMetaData>) metaDataMapField.get(null);
+ for (Entry<F, FieldMetaData> metaDataEntry : metaDataMap.entrySet()) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("processing field " + metaDataEntry.getValue().fieldName);
+ }
+ Object fieldValue = instance.getFieldValue(metaDataEntry.getKey());
+ if (fieldValue != null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("field " + metaDataEntry.getValue().fieldName + " has a default value ["
+ + fieldValue + "], calling setter to force the field to be set");
+ }
+ instance.setFieldValue(metaDataEntry.getKey(), fieldValue);
+ }
+ }
+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
+ MappingUtils.throwMappingException(e);
+ }
+ }
+
+}
diff --git a/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/utils/ObjectMapperSingleton.java b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/utils/ObjectMapperSingleton.java
index ab5ffe9..16896e3 100644
--- a/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/utils/ObjectMapperSingleton.java
+++ b/modules/registry/registry-core/src/main/java/org/apache/airavata/registry/core/utils/ObjectMapperSingleton.java
@@ -82,4 +82,4 @@ public class ObjectMapperSingleton extends DozerBeanMapper{
return false;
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/registry/registry-core/src/main/resources/dozer_mapping.xml b/modules/registry/registry-core/src/main/resources/dozer_mapping.xml
index 5d3e9b6..d9cc624 100644
--- a/modules/registry/registry-core/src/main/resources/dozer_mapping.xml
+++ b/modules/registry/registry-core/src/main/resources/dozer_mapping.xml
@@ -24,6 +24,11 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
+
+ <configuration>
+ <bean-factory>org.apache.airavata.registry.core.utils.CustomBeanFactory</bean-factory>
+ </configuration>
+
<mapping>
<class-a>org.apache.airavata.model.appcatalog.storageresource.StorageResourceDescription</class-a>
<class-b>org.apache.airavata.registry.core.entities.appcatalog.StorageInterfaceEntity</class-b>
diff --git a/modules/registry/registry-core/src/test/java/org/apache/airavata/registry/core/utils/CustomBeanFactoryTest.java b/modules/registry/registry-core/src/test/java/org/apache/airavata/registry/core/utils/CustomBeanFactoryTest.java
new file mode 100644
index 0000000..27cd5b9
--- /dev/null
+++ b/modules/registry/registry-core/src/test/java/org/apache/airavata/registry/core/utils/CustomBeanFactoryTest.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.airavata.registry.core.utils;
+
+import org.apache.airavata.model.experiment.UserConfigurationDataModel;
+import org.apache.thrift.TFieldRequirementType;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CustomBeanFactoryTest {
+
+ @Test
+ public void testRequiredFieldWithDefault() {
+ Assert.assertEquals(TFieldRequirementType.REQUIRED, UserConfigurationDataModel.metaDataMap.get(UserConfigurationDataModel._Fields.AIRAVATA_AUTO_SCHEDULE).requirementType);
+ UserConfigurationDataModel fromConstructor = new UserConfigurationDataModel();
+ Assert.assertFalse(fromConstructor.isSetAiravataAutoSchedule());
+
+ CustomBeanFactory customBeanFactory = new CustomBeanFactory();
+ UserConfigurationDataModel fromFactory = (UserConfigurationDataModel) customBeanFactory
+ .createBean(null, null, UserConfigurationDataModel.class.getName());
+ Assert.assertTrue(fromFactory.isSetAiravataAutoSchedule());
+ }
+
+ @Test
+ public void testOptionalFieldWithDefault() {
+ Assert.assertEquals(TFieldRequirementType.OPTIONAL, UserConfigurationDataModel.metaDataMap.get(UserConfigurationDataModel._Fields.SHARE_EXPERIMENT_PUBLICLY).requirementType);
+ UserConfigurationDataModel fromConstructor = new UserConfigurationDataModel();
+ Assert.assertFalse(fromConstructor.isSetShareExperimentPublicly());
+
+ CustomBeanFactory customBeanFactory = new CustomBeanFactory();
+ UserConfigurationDataModel fromFactory = (UserConfigurationDataModel) customBeanFactory
+ .createBean(null, null, UserConfigurationDataModel.class.getName());
+ Assert.assertTrue(fromFactory.isSetShareExperimentPublicly());
+ }
+}
diff --git a/modules/registry/registry-core/src/test/java/org/apache/airavata/registry/core/utils/ObjectMapperSingletonTest.java b/modules/registry/registry-core/src/test/java/org/apache/airavata/registry/core/utils/ObjectMapperSingletonTest.java
new file mode 100644
index 0000000..9d29b84
--- /dev/null
+++ b/modules/registry/registry-core/src/test/java/org/apache/airavata/registry/core/utils/ObjectMapperSingletonTest.java
@@ -0,0 +1,66 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.airavata.registry.core.utils;
+
+import org.apache.airavata.model.experiment.UserConfigurationDataModel;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ObjectMapperSingletonTest {
+
+ public static class TestUserConfigurationDataModel {
+ private boolean airavataAutoSchedule;
+
+ public boolean isAiravataAutoSchedule() {
+ return airavataAutoSchedule;
+ }
+
+ public void setAiravataAutoSchedule(boolean airavataAutoSchedule) {
+ this.airavataAutoSchedule = airavataAutoSchedule;
+ }
+ }
+
+ @Test
+ public void testCopyBooleanFieldsWithDefaultValue() {
+
+ TestUserConfigurationDataModel testUserConfigurationDataModel = new TestUserConfigurationDataModel();
+ testUserConfigurationDataModel.setAiravataAutoSchedule(false);
+
+ // Make sure these fields have default values
+ Assert.assertNotNull("airavataAutoSchedule has default value", new UserConfigurationDataModel().getFieldValue(UserConfigurationDataModel._Fields.AIRAVATA_AUTO_SCHEDULE));
+ Assert.assertNotNull("overrideManualScheduledParams has default value", new UserConfigurationDataModel().getFieldValue(UserConfigurationDataModel._Fields.OVERRIDE_MANUAL_SCHEDULED_PARAMS));
+ Assert.assertNotNull("shareExperimentPublicly has default value", new UserConfigurationDataModel().getFieldValue(UserConfigurationDataModel._Fields.SHARE_EXPERIMENT_PUBLICLY));
+ UserConfigurationDataModel userConfigurationDataModel = ObjectMapperSingleton.getInstance()
+ .map(testUserConfigurationDataModel, UserConfigurationDataModel.class);
+
+ Assert.assertTrue(userConfigurationDataModel.isSetAiravataAutoSchedule());
+ Assert.assertFalse(userConfigurationDataModel.isAiravataAutoSchedule());
+ Assert.assertTrue(
+ "even though overrideManualScheduledParams isn't a field on the source object, since it has a default value it should be set",
+ userConfigurationDataModel.isSetOverrideManualScheduledParams());
+ Assert.assertFalse(userConfigurationDataModel.isOverrideManualScheduledParams());
+ Assert.assertTrue(
+ "even though shareExperimentPublicly isn't a field on the source object, since it has a default value it should be set",
+ userConfigurationDataModel.isSetShareExperimentPublicly());
+ Assert.assertFalse(userConfigurationDataModel.isShareExperimentPublicly());
+ }
+}