http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java b/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java new file mode 100644 index 0000000..cec46f6 --- /dev/null +++ b/common/src/main/java/org/apache/sqoop/json/util/ConfigSerialization.java @@ -0,0 +1,226 @@ +/** + * 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.sqoop.json.util; + +import org.apache.commons.lang.StringUtils; +import org.apache.sqoop.common.SqoopException; +import org.apache.sqoop.model.MBooleanInput; +import org.apache.sqoop.model.MEnumInput; +import org.apache.sqoop.model.MConfig; +import org.apache.sqoop.model.MConfigType; +import org.apache.sqoop.model.MInput; +import org.apache.sqoop.model.MInputType; +import org.apache.sqoop.model.MIntegerInput; +import org.apache.sqoop.model.MMapInput; +import org.apache.sqoop.model.MStringInput; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Convenient static methods for serializing config objects. + */ +public final class ConfigSerialization { + + public static final String ALL = "all"; + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String VERSION = "version"; + public static final String CLASS = "class"; + public static final String ENABLED = "enabled"; + public static final String CREATION_USER = "creation-user"; + public static final String CREATION_DATE = "creation-date"; + public static final String UPDATE_USER = "update-user"; + public static final String UPDATE_DATE = "update-date"; + // TODO(VB): Move these constants to connector bean + public static final String CONNECTOR_LINK_CONFIG = "link-config"; + public static final String CONNECTOR_JOB_CONFIG = "job-config"; + // TODO:move these configs to driver bean + public static final String DRIVER_VERSION = "driver-version"; + public static final String DRIVER_CONFIG = "driver-config"; + + public static final String CONFIG_NAME = "name"; + public static final String CONFIG_TYPE = "type"; + public static final String CONFIG_INPUTS = "inputs"; + public static final String CONFIG_INPUT_NAME = "name"; + public static final String CONFIG_INPUT_TYPE = "type"; + public static final String CONFIG_INPUT_SENSITIVE = "sensitive"; + public static final String CONFIG_INPUT_SIZE = "size"; + public static final String CONFIG_INPUT_VALUE = "value"; + public static final String CONFIG_INPUT_VALUES = "values"; + + /** + * Transform given list of configs to JSON Array object. + * + * @param mConfigs List of configs. + * @return JSON object with serialized config of the list. + */ + @SuppressWarnings("unchecked") + public static JSONArray extractConfigList(List<MConfig> mConfigs, boolean skipSensitive) { + JSONArray configs = new JSONArray(); + + for (MConfig mConfig : mConfigs) { + configs.add(extractConfig(mConfig, skipSensitive)); + } + + return configs; + } + + /** + * Transform given config to JSON Object. + * + * @param mConfig Given MConfig instance + * @param skipSensitive conditionally add sensitive input values + * @return Serialized JSON object. + */ + @SuppressWarnings("unchecked") + static JSONObject extractConfig(MConfig mConfig, boolean skipSensitive) { + JSONObject config = new JSONObject(); + config.put(ID, mConfig.getPersistenceId()); + config.put(CONFIG_NAME, mConfig.getName()); + config.put(CONFIG_TYPE, MConfigType.LINK.toString()); + JSONArray mInputs = new JSONArray(); + config.put(CONFIG_INPUTS, mInputs); + + for (MInput<?> mInput : mConfig.getInputs()) { + JSONObject input = new JSONObject(); + input.put(ID, mInput.getPersistenceId()); + input.put(CONFIG_INPUT_NAME, mInput.getName()); + input.put(CONFIG_INPUT_TYPE, mInput.getType().toString()); + input.put(CONFIG_INPUT_SENSITIVE, mInput.isSensitive()); + + // String specific serialization + if (mInput.getType() == MInputType.STRING) { + input.put(CONFIG_INPUT_SIZE, + ((MStringInput)mInput).getMaxLength()); + } + + // Enum specific serialization + if(mInput.getType() == MInputType.ENUM) { + input.put(CONFIG_INPUT_VALUES, + StringUtils.join(((MEnumInput)mInput).getValues(), ",")); + } + + // Serialize value if is there + // Skip if sensitive + if (!mInput.isEmpty() && !(skipSensitive && mInput.isSensitive())) { + if (mInput.getType() == MInputType.MAP) { + input.put(CONFIG_INPUT_VALUE, mInput.getValue()); + } else { + input.put(CONFIG_INPUT_VALUE, mInput.getUrlSafeValueString()); + } + } + + mInputs.add(input); + } + + return config; + } + + /** + * Restore List of MConfigs from JSON Array. + * + * @param configs JSON array representing list of MConfigs + * @return Restored list of MConfigs + */ + public static List<MConfig> restoreConfigList(JSONArray configs) { + List<MConfig> mConfigs = new ArrayList<MConfig>(); + + for (int i = 0; i < configs.size(); i++) { + mConfigs.add(restoreConfig((JSONObject) configs.get(i))); + } + + return mConfigs; + } + + /** + * Restore one MConfig from JSON Object. + * + * @param config JSON representation of the MConfig. + * @return Restored MConfig. + */ + static MConfig restoreConfig(JSONObject config) { + JSONArray inputs = (JSONArray) config.get(CONFIG_INPUTS); + + List<MInput<?>> mInputs = new ArrayList<MInput<?>>(); + for (int i = 0; i < inputs.size(); i++) { + JSONObject input = (JSONObject) inputs.get(i); + MInputType type = + MInputType.valueOf((String) input.get(CONFIG_INPUT_TYPE)); + String name = (String) input.get(CONFIG_INPUT_NAME); + Boolean sensitive = (Boolean) input.get(CONFIG_INPUT_SENSITIVE); + MInput mInput = null; + switch (type) { + case STRING: { + long size = (Long) input.get(CONFIG_INPUT_SIZE); + mInput = new MStringInput(name, sensitive.booleanValue(), (short) size); + break; + } + case MAP: { + mInput = new MMapInput(name, sensitive.booleanValue()); + break; + } + case INTEGER: { + mInput = new MIntegerInput(name, sensitive.booleanValue()); + break; + } + case BOOLEAN: { + mInput = new MBooleanInput(name, sensitive.booleanValue()); + break; + } + case ENUM: { + String values = (String) input.get(CONFIG_INPUT_VALUES); + mInput = new MEnumInput(name, sensitive.booleanValue(), values.split(",")); + break; + } + } + + // Propagate config ID + mInput.setPersistenceId((Long)input.get(ID)); + + // Propagate config optional value + if(input.containsKey(CONFIG_INPUT_VALUE)) { + switch (type) { + case MAP: + try { + mInput.setValue((Map<String, String>)input.get(CONFIG_INPUT_VALUE)); + } catch (ClassCastException e) { + throw new SqoopException(SerializationError.SERIALIZATION_001, name + " requires a 'map' value."); + } + break; + default: + mInput.restoreFromUrlSafeValueString( + (String) input.get(CONFIG_INPUT_VALUE)); + break; + } + } + mInputs.add(mInput); + } + + MConfig mConfig = new MConfig((String) config.get(CONFIG_NAME), mInputs); + mConfig.setPersistenceId((Long) config.get(ID)); + return mConfig; + } + + private ConfigSerialization() { + // Do not instantiate + } +}
http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java b/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java deleted file mode 100644 index 77f6191..0000000 --- a/common/src/main/java/org/apache/sqoop/json/util/FormSerialization.java +++ /dev/null @@ -1,223 +0,0 @@ -/** - * 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.sqoop.json.util; - -import org.apache.commons.lang.StringUtils; -import org.apache.sqoop.common.SqoopException; -import org.apache.sqoop.model.MBooleanInput; -import org.apache.sqoop.model.MEnumInput; -import org.apache.sqoop.model.MForm; -import org.apache.sqoop.model.MFormType; -import org.apache.sqoop.model.MInput; -import org.apache.sqoop.model.MInputType; -import org.apache.sqoop.model.MIntegerInput; -import org.apache.sqoop.model.MMapInput; -import org.apache.sqoop.model.MStringInput; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Convenient static methods for serializing forms. - */ -public final class FormSerialization { - - public static final String ALL = "all"; - public static final String ID = "id"; - public static final String NAME = "name"; - public static final String VERSION = "version"; - public static final String DRIVER_VERSION = "driver-version"; - public static final String CLASS = "class"; - public static final String ENABLED = "enabled"; - public static final String CREATION_USER = "creation-user"; - public static final String CREATION_DATE = "creation-date"; - public static final String UPDATE_USER = "update-user"; - public static final String UPDATE_DATE = "update-date"; - public static final String CON_FORMS = "con-forms"; - public static final String JOB_FORMS = "job-forms"; - - public static final String FORM_NAME = "name"; - public static final String FORM_TYPE = "type"; - public static final String FORM_INPUTS = "inputs"; - public static final String FORM_INPUT_NAME = "name"; - public static final String FORM_INPUT_TYPE = "type"; - public static final String FORM_INPUT_SENSITIVE = "sensitive"; - public static final String FORM_INPUT_SIZE = "size"; - public static final String FORM_INPUT_VALUE = "value"; - public static final String FORM_INPUT_VALUES = "values"; - - /** - * Transform given list of forms to JSON Array object. - * - * @param mForms List of forms. - * @return JSON object with serialized form of the list. - */ - @SuppressWarnings("unchecked") - public static JSONArray extractForms(List<MForm> mForms, boolean skipSensitive) { - JSONArray forms = new JSONArray(); - - for (MForm mForm : mForms) { - forms.add(extractForm(mForm, skipSensitive)); - } - - return forms; - } - - /** - * Transform given form to JSON Object. - * - * @param mForm Given MForm instance - * @param skipSensitive conditionally add sensitive input values - * @return Serialized JSON object. - */ - @SuppressWarnings("unchecked") - public static JSONObject extractForm(MForm mForm, boolean skipSensitive) { - JSONObject form = new JSONObject(); - form.put(ID, mForm.getPersistenceId()); - form.put(FORM_NAME, mForm.getName()); - form.put(FORM_TYPE, MFormType.CONNECTION.toString()); - JSONArray mInputs = new JSONArray(); - form.put(FORM_INPUTS, mInputs); - - for (MInput<?> mInput : mForm.getInputs()) { - JSONObject input = new JSONObject(); - input.put(ID, mInput.getPersistenceId()); - input.put(FORM_INPUT_NAME, mInput.getName()); - input.put(FORM_INPUT_TYPE, mInput.getType().toString()); - input.put(FORM_INPUT_SENSITIVE, mInput.isSensitive()); - - // String specific serialization - if (mInput.getType() == MInputType.STRING) { - input.put(FORM_INPUT_SIZE, - ((MStringInput)mInput).getMaxLength()); - } - - // Enum specific serialization - if(mInput.getType() == MInputType.ENUM) { - input.put(FORM_INPUT_VALUES, - StringUtils.join(((MEnumInput)mInput).getValues(), ",")); - } - - // Serialize value if is there - // Skip if sensitive - if (!mInput.isEmpty() && !(skipSensitive && mInput.isSensitive())) { - if (mInput.getType() == MInputType.MAP) { - input.put(FORM_INPUT_VALUE, mInput.getValue()); - } else { - input.put(FORM_INPUT_VALUE, mInput.getUrlSafeValueString()); - } - } - - mInputs.add(input); - } - - return form; - } - - /** - * Restore List of MForms from JSON Array. - * - * @param forms JSON array representing list of MForms - * @return Restored list of MForms - */ - public static List<MForm> restoreForms(JSONArray forms) { - List<MForm> mForms = new ArrayList<MForm>(); - - for (int i = 0; i < forms.size(); i++) { - mForms.add(restoreForm((JSONObject) forms.get(i))); - } - - return mForms; - } - - /** - * Restore one MForm from JSON Object. - * - * @param form JSON representation of the MForm. - * @return Restored MForm. - */ - public static MForm restoreForm(JSONObject form) { - JSONArray inputs = (JSONArray) form.get(FORM_INPUTS); - - List<MInput<?>> mInputs = new ArrayList<MInput<?>>(); - for (int i = 0; i < inputs.size(); i++) { - JSONObject input = (JSONObject) inputs.get(i); - MInputType type = - MInputType.valueOf((String) input.get(FORM_INPUT_TYPE)); - String name = (String) input.get(FORM_INPUT_NAME); - Boolean sensitive = (Boolean) input.get(FORM_INPUT_SENSITIVE); - MInput mInput = null; - switch (type) { - case STRING: { - long size = (Long) input.get(FORM_INPUT_SIZE); - mInput = new MStringInput(name, sensitive.booleanValue(), (short) size); - break; - } - case MAP: { - mInput = new MMapInput(name, sensitive.booleanValue()); - break; - } - case INTEGER: { - mInput = new MIntegerInput(name, sensitive.booleanValue()); - break; - } - case BOOLEAN: { - mInput = new MBooleanInput(name, sensitive.booleanValue()); - break; - } - case ENUM: { - String values = (String) input.get(FORM_INPUT_VALUES); - mInput = new MEnumInput(name, sensitive.booleanValue(), values.split(",")); - break; - } - } - - // Propagate form ID - mInput.setPersistenceId((Long)input.get(ID)); - - // Propagate form optional value - if(input.containsKey(FORM_INPUT_VALUE)) { - switch (type) { - case MAP: - try { - mInput.setValue((Map<String, String>)input.get(FORM_INPUT_VALUE)); - } catch (ClassCastException e) { - throw new SqoopException(SerializationError.SERIALIZATION_001, name + " requires a 'map' value."); - } - break; - default: - mInput.restoreFromUrlSafeValueString( - (String) input.get(FORM_INPUT_VALUE)); - break; - } - } - mInputs.add(mInput); - } - - MForm mForm = new MForm((String) form.get(FORM_NAME), mInputs); - mForm.setPersistenceId((Long) form.get(ID)); - return mForm; - } - - private FormSerialization() { - // Do not instantiate - } -} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java b/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java index 20ee5f3..f27d81d 100644 --- a/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java +++ b/common/src/main/java/org/apache/sqoop/json/util/ResourceBundleSerialization.java @@ -43,7 +43,6 @@ public final class ResourceBundleSerialization { for (ResourceBundle bundle : bundles) { array.add(extractResourceBundle(bundle)); } - return array; } @@ -58,7 +57,6 @@ public final class ResourceBundleSerialization { return json; } - @SuppressWarnings("unchecked") public static List<ResourceBundle> restoreResourceBundles(JSONArray array) { List<ResourceBundle> bundles = new LinkedList<ResourceBundle>(); for (Object item : array) { http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/Config.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/Config.java b/common/src/main/java/org/apache/sqoop/model/Config.java new file mode 100644 index 0000000..b4d847d --- /dev/null +++ b/common/src/main/java/org/apache/sqoop/model/Config.java @@ -0,0 +1,34 @@ +/** + * 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.sqoop.model; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Denote config in Configuration class + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface Config { + /** + * Optional name for the form object + * + * @return + */ + String name() default ""; +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/ConfigClass.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/ConfigClass.java b/common/src/main/java/org/apache/sqoop/model/ConfigClass.java new file mode 100644 index 0000000..f925759 --- /dev/null +++ b/common/src/main/java/org/apache/sqoop/model/ConfigClass.java @@ -0,0 +1,45 @@ +/** + * 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.sqoop.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denote configuration class + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ConfigClass { + + /** + * Default size for Inputs in this config. + * + * @return + */ + short defaultSize() default -1; + + /** + * List of validators associated with this config. + * + * @return + */ + Validator[] validators() default {}; +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java b/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java new file mode 100644 index 0000000..9e762dc --- /dev/null +++ b/common/src/main/java/org/apache/sqoop/model/ConfigUtils.java @@ -0,0 +1,637 @@ +/** + * 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.sqoop.model; + +import org.apache.commons.lang.StringUtils; +import org.apache.sqoop.common.SqoopException; +import org.apache.sqoop.utils.ClassUtils; +import org.apache.sqoop.validation.Message; +import org.apache.sqoop.validation.Status; +import org.apache.sqoop.validation.ConfigValidator; +import org.apache.sqoop.validation.ConfigValidationResult; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * Util class for transforming data from correctly annotated configuration + * objects to different structures and vice-versa. + * + * TODO: This class should see some overhaul into more reusable code, especially expose and re-use the methods at the end. + */ +public class ConfigUtils { + + /** + * Transform correctly annotated configuration object to corresponding + * list of configs. + * + * Configs will be order according to the occurrence in the configuration + * class. Inputs will be also ordered based on occurrence. + * + * @param configuration Annotated arbitrary configuration object + * @return Corresponding list of configs + */ + public static List<MConfig> toConfigs(Object configuration) { + return toConfigs(configuration.getClass(), configuration); + } + + public static List<MConfig> toConfigs(Class klass) { + return toConfigs(klass, null); + } + + @SuppressWarnings("unchecked") + public static List<MConfig> toConfigs(Class klass, Object configuration) { + Set<String> formNames = new HashSet<String>(); + + ConfigurationClass configurationClass = + (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class); + + // Each configuration object must have this class annotation + if(configurationClass == null) { + throw new SqoopException(ModelError.MODEL_003, + "Missing annotation ConfigurationClass on class " + klass.getName()); + } + + List<MConfig> configs = new LinkedList<MConfig>(); + + // Iterate over all declared fields + for (Field field : klass.getDeclaredFields()) { + field.setAccessible(true); + + // Each field that should be part of user input should have Input + // annotation. + Config formAnnotation = field.getAnnotation(Config.class); + + if (formAnnotation != null) { + String formName = getFormName(field, formAnnotation, formNames); + + Class type = field.getType(); + + Object value = null; + if(configuration != null) { + try { + value = field.get(configuration); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, + "Can't retrieve value from " + field.getName(), e); + } + } + + configs.add(toConfig(formName, type, value)); + } + } + + return configs; + } + + @SuppressWarnings("unchecked") + private static MConfig toConfig(String formName, Class klass, Object object) { + ConfigClass global = + (ConfigClass)klass.getAnnotation(ConfigClass.class); + + // Each configuration object must have this class annotation + if(global == null) { + throw new SqoopException(ModelError.MODEL_003, + "Missing annotation ConfigClass on class " + klass.getName()); + } + + // Intermediate list of inputs + List<MInput<?>> inputs = new LinkedList<MInput<?>>(); + + // Iterate over all declared fields + for (Field field : klass.getDeclaredFields()) { + field.setAccessible(true); + + String fieldName = field.getName(); + String inputName = formName + "." + fieldName; + + // Each field that should be part of user input should have Input + // annotation. + Input inputAnnotation = field.getAnnotation(Input.class); + + if(inputAnnotation != null) { + boolean sensitive = inputAnnotation.sensitive(); + short maxLen = inputAnnotation.size(); + Class<?> type = field.getType(); + + MInput input; + + // We need to support NULL, so we do not support primitive types + if(type.isPrimitive()) { + throw new SqoopException(ModelError.MODEL_007, + "Detected primitive type " + type + " for field " + fieldName); + } + + // Instantiate corresponding MInput<?> structure + if (type == String.class) { + input = new MStringInput(inputName, sensitive, maxLen); + } else if (type.isAssignableFrom(Map.class)) { + input = new MMapInput(inputName, sensitive); + } else if (type == Integer.class) { + input = new MIntegerInput(inputName, sensitive); + } else if (type == Boolean.class) { + input = new MBooleanInput(inputName, sensitive); + } else if (type.isEnum()) { + input = new MEnumInput(inputName, sensitive, + ClassUtils.getEnumStrings(type)); + } else { + throw new SqoopException(ModelError.MODEL_004, "Unsupported type " + + type.getName() + " for input " + fieldName); + } + + // Move value if it's present in original configuration object + if(object != null) { + Object value; + try { + value = field.get(object); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, + "Can't retrieve value from " + field.getName(), e); + } + if(value == null) { + input.setEmpty(); + } else { + input.setValue(value); + } + } + + inputs.add(input); + } + } + + return new MConfig(formName, inputs); + } + + private static Field getFieldFromName(Class<?> klass, String name) { + Field formField; + try { + formField = klass.getDeclaredField(name); + } catch (NoSuchFieldException e) { + // reverse lookup form field from custom form name + if (name != null) { + for (Field field : klass.getDeclaredFields()) { + Config formAnnotation = field.getAnnotation(Config.class); + if (formAnnotation == null) { + continue; + } + if (!StringUtils.isEmpty(formAnnotation.name()) && name.equals(formAnnotation.name())) { + return field; + } + } + } + throw new SqoopException(ModelError.MODEL_006, "Missing field " + name + " on form class " + + klass.getCanonicalName(), e); + } + return formField; + } + + /** + * Move config values from config list into corresponding configuration object. + * + * @param configs Input config list + * @param configuration Output configuration object + */ + public static void fromConfigs(List<MConfig> configs, Object configuration) { + Class klass = configuration.getClass(); + + for(MConfig config : configs) { + Field configField; + try { + configField = klass.getDeclaredField(config.getName()); + } catch (NoSuchFieldException e) { + throw new SqoopException(ModelError.MODEL_006, + "Missing field " + config.getName() + " on config class " + klass.getCanonicalName(), e); + } + + Field formField = getFieldFromName(klass, config.getName()); + // We need to access this field even if it would be declared as private + configField.setAccessible(true); + Class configClass = configField.getType(); + Object newValue = ClassUtils.instantiate(configClass); + + if (newValue == null) { + throw new SqoopException(ModelError.MODEL_006, + "Can't instantiate new config " + configClass); + } + + for(MInput input : config.getInputs()) { + String[] splitNames = input.getName().split("\\."); + if (splitNames.length != 2) { + throw new SqoopException(ModelError.MODEL_009, "Invalid name: " + + input.getName()); + } + + String inputName = splitNames[1]; + // TODO(jarcec): Names structures fix, handle error cases + Field inputField; + try { + inputField = configClass.getDeclaredField(inputName); + } catch (NoSuchFieldException e) { + throw new SqoopException(ModelError.MODEL_006, "Missing field " + + input.getName(), e); + } + + // We need to access this field even if it would be declared as private + inputField.setAccessible(true); + + try { + if (input.isEmpty()) { + inputField.set(newValue, null); + } else { + if (input.getType() == MInputType.ENUM) { + inputField.set(newValue, Enum.valueOf( + (Class<? extends Enum>) inputField.getType(), + (String) input.getValue())); + } else { + inputField.set(newValue, input.getValue()); + } + } + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, "Issue with field " + + inputField.getName(), e); + } + } + + try { + configField.set(configuration, newValue); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, + "Issue with field " + configField.getName(), e); + } + } + } + + /** + * Apply validations on the configs. + * + * @param configs Configs that should be updated + * @param validation Validation that we should apply + */ + public static void applyValidation(List<MConfig> configs, ConfigValidator validation) { + Map<ConfigValidator.ConfigInput, ConfigValidator.Message> messages = validation.getMessages(); + + for(MConfig config : configs) { + applyValidation(config, messages); + + for(MInput input : config.getInputs()) { + applyValidation(input, messages); + } + } + } + + /** + * Apply validation on given validated element. + * + * @param element + * Element on what we're applying the validations + * @param messages + * Map of all validation messages + */ + public static void applyValidation(MValidatedElement element, Map<ConfigValidator.ConfigInput, ConfigValidator.Message> messages) { + ConfigValidator.ConfigInput name = new ConfigValidator.ConfigInput(element.getName()); + + if(messages.containsKey(name)) { + ConfigValidator.Message message = messages.get(name); + element.addValidationMessage(new Message(message.getStatus(), message.getMessage())); + } else { + element.addValidationMessage(new Message(Status.getDefault(), null)); + } + } + + + /** + * Apply given validations on list of configs. + * + * @param configs + * @param result + */ + public static void applyValidation(List<MConfig> configs, ConfigValidationResult result) { + for(MConfig config : configs) { + applyValidation(config, result); + + for(MInput input : config.getInputs()) { + applyValidation(input, result); + } + } + } + + /** + * Apply validation messages on given element. + * + * Element's state will be set to default if there are no associated messages. + * + * @param element + * @param result + */ + public static void applyValidation(MValidatedElement element, ConfigValidationResult result) { + List<Message> messages = result.getMessages().get(element.getName()); + + if(messages != null) { + element.setValidationMessages(messages); + } else { + element.resetValidationMessages(); + } + } + + /** + * Convert configuration object to JSON. Only filled properties are serialized, + * properties with null value are skipped. + * + * @param configuration Correctly annotated configuration object + * @return String of JSON representation + */ + @SuppressWarnings("unchecked") + public static String toJson(Object configuration) { + Class klass = configuration.getClass(); + Set<String> formNames = new HashSet<String>(); + ConfigurationClass configurationClass = + (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class); + + // Each configuration object must have this class annotation + if(configurationClass == null) { + throw new SqoopException(ModelError.MODEL_003, + "Missing annotation ConfigurationGroup on class " + klass.getName()); + } + + JSONObject jsonOutput = new JSONObject(); + + // Iterate over all declared fields + for (Field formField : klass.getDeclaredFields()) { + formField.setAccessible(true); + + // We're processing only config validations + Config formAnnotation = formField.getAnnotation(Config.class); + if(formAnnotation == null) { + continue; + } + String formName = getFormName(formField, formAnnotation, formNames); + + Object formValue; + try { + formValue = formField.get(configuration); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, "Issue with field " + + formName, e); + } + + JSONObject jsonConfig = new JSONObject(); + + // Now process each input on the config + for(Field inputField : formField.getType().getDeclaredFields()) { + inputField.setAccessible(true); + String inputName = inputField.getName(); + + Object value; + try { + value = inputField.get(formValue); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, "Issue with field " + + formName + "." + inputName, e); + } + + Input inputAnnotation = inputField.getAnnotation(Input.class); + + // Do not serialize all values + if (inputAnnotation != null && value != null) { + Class<?> type = inputField.getType(); + + // We need to support NULL, so we do not support primitive types + if (type.isPrimitive()) { + throw new SqoopException(ModelError.MODEL_007, + "Detected primitive type " + type + " for field " + formName + + "." + inputName); + } + + if(type == String.class) { + jsonConfig.put(inputName, value); + } else if (type.isAssignableFrom(Map.class)) { + JSONObject map = new JSONObject(); + for (Object key : ((Map) value).keySet()) { + map.put(key, ((Map) value).get(key)); + } + jsonConfig.put(inputName, map); + } else if(type == Integer.class) { + jsonConfig.put(inputName, value); + } else if(type.isEnum()) { + jsonConfig.put(inputName, value.toString()); + } else if(type == Boolean.class) { + jsonConfig.put(inputName, value); + }else { + throw new SqoopException(ModelError.MODEL_004, + "Unsupported type " + type.getName() + " for input " + formName + "." + inputName); + } + } + } + + jsonOutput.put(formName, jsonConfig); + } + return jsonOutput.toJSONString(); + } + + /** + * Parse given input JSON string and move it's values to given configuration + * object. + * + * @param json JSON representation of the configuration object + * @param configuration ConfigurationGroup object to be filled + */ + public static void fillValues(String json, Object configuration) { + Class klass = configuration.getClass(); + + Set<String> formNames = new HashSet<String>(); + JSONObject jsonConfigs = (JSONObject) JSONValue.parse(json); + + for(Field configField : klass.getDeclaredFields()) { + configField.setAccessible(true); + String configName = configField.getName(); + + // We're processing only config validations + Config formAnnotation = configField.getAnnotation(Config.class); + if(formAnnotation == null) { + continue; + } + String formName = getFormName(configField, formAnnotation, formNames); + + try { + configField.set(configuration, configField.getType().newInstance()); + } catch (Exception e) { + throw new SqoopException(ModelError.MODEL_005, + "Issue with field " + configName, e); + } + + JSONObject jsonInputs = (JSONObject) jsonConfigs.get(configField.getName()); + if(jsonInputs == null) { + continue; + } + + Object configValue; + try { + configValue = configField.get(configuration); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, + "Issue with field " + configName, e); + } + + for(Field inputField : configField.getType().getDeclaredFields()) { + inputField.setAccessible(true); + String inputName = inputField.getName(); + + Input inputAnnotation = inputField.getAnnotation(Input.class); + + if(inputAnnotation == null || jsonInputs.get(inputName) == null) { + try { + inputField.set(configValue, null); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, + "Issue with field " + configName + "." + inputName, e); + } + continue; + } + + Class type = inputField.getType(); + + try { + if(type == String.class) { + inputField.set(configValue, jsonInputs.get(inputName)); + } else if (type.isAssignableFrom(Map.class)) { + Map<String, String> map = new HashMap<String, String>(); + JSONObject jsonObject = (JSONObject) jsonInputs.get(inputName); + for(Object key : jsonObject.keySet()) { + map.put((String)key, (String)jsonObject.get(key)); + } + inputField.set(configValue, map); + } else if(type == Integer.class) { + inputField.set(configValue, ((Long)jsonInputs.get(inputName)).intValue()); + } else if(type.isEnum()) { + inputField.set(configValue, Enum.valueOf((Class<? extends Enum>) inputField.getType(), (String) jsonInputs.get(inputName))); + } else if(type == Boolean.class) { + inputField.set(configValue, (Boolean) jsonInputs.get(inputName)); + }else { + throw new SqoopException(ModelError.MODEL_004, + "Unsupported type " + type.getName() + " for input " + configName + "." + inputName); + } + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_005, + "Issue with field " + configName + "." + inputName, e); + } + } + } + } + + private static String getFormName(Field member, Config annotation, Set<String> existingFormNames) { + if (StringUtils.isEmpty(annotation.name())) { + return member.getName(); + } else { + checkForValidFormName(existingFormNames, annotation.name()); + existingFormNames.add(annotation.name()); + return annotation.name(); + } + } + + private static void checkForValidFormName(Set<String> existingFormNames, + String customFormName) { + // uniqueness across fields check + if (existingFormNames.contains(customFormName)) { + throw new SqoopException(ModelError.MODEL_012, + "Issue with field form name " + customFormName); + } + + if (!Character.isJavaIdentifierStart(customFormName.toCharArray()[0])) { + throw new SqoopException(ModelError.MODEL_013, + "Issue with field form name " + customFormName); + } + for (Character c : customFormName.toCharArray()) { + if (Character.isJavaIdentifierPart(c)) + continue; + throw new SqoopException(ModelError.MODEL_013, + "Issue with field form name " + customFormName); + } + + if (customFormName.length() > 30) { + throw new SqoopException(ModelError.MODEL_014, + "Issue with field form name " + customFormName); + + } + } + + public static String getName(Field input, Input annotation) { + return input.getName(); + } + + public static String getName(Field config, Config annotation) { + return config.getName(); + } + + public static ConfigurationClass getConfigurationClassAnnotation(Object object, boolean strict) { + ConfigurationClass annotation = object.getClass().getAnnotation(ConfigurationClass.class); + + if(strict && annotation == null) { + throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationGroupClass on class " + object.getClass().getName()); + } + + return annotation; + } + + public static ConfigClass getConfigClassAnnotation(Object object, boolean strict) { + ConfigClass annotation = object.getClass().getAnnotation(ConfigClass.class); + + if(strict && annotation == null) { + throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationGroupClass on class " + object.getClass().getName()); + } + + return annotation; + } + + public static Config getConfigAnnotation(Field field, boolean strict) { + Config annotation = field.getAnnotation(Config.class); + + if(strict && annotation == null) { + throw new SqoopException(ModelError.MODEL_003, "Missing annotation Config on Field " + field.getName() + " on class " + field.getDeclaringClass().getName()); + } + + return annotation; + } + + public static Input getInputAnnotation(Field field, boolean strict) { + Input annotation = field.getAnnotation(Input.class); + + if(strict && annotation == null) { + throw new SqoopException(ModelError.MODEL_003, "Missing annotation Input on Field " + field.getName() + " on class " + field.getDeclaringClass().getName()); + } + + return annotation; + } + + public static Object getFieldValue(Field field, Object object) { + try { + field.setAccessible(true); + return field.get(object); + } catch (IllegalAccessException e) { + throw new SqoopException(ModelError.MODEL_015, e); + } + } + +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java b/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java index 73374d8..c65c478 100644 --- a/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java +++ b/common/src/main/java/org/apache/sqoop/model/ConfigurationClass.java @@ -23,15 +23,16 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Class annotation. Each class that is used a configuration object where user - * is expected to provide input need to have this annotation. + * Class annotation to represent configuration for the connectors + * Each class that is used a configuration group object, the connector developer + * is expected to provide the inputs needed for this annotation */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ConfigurationClass { /** - * List of validators associated with this Configuration class. + * List of validators associated with this Configuration group class. * * @return */ http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/Form.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/Form.java b/common/src/main/java/org/apache/sqoop/model/Form.java deleted file mode 100644 index 27a0831..0000000 --- a/common/src/main/java/org/apache/sqoop/model/Form.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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.sqoop.model; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Denote form in Configuration class - */ -@Retention(RetentionPolicy.RUNTIME) -public @interface Form { - - /** - * Optional name for the form object - * - * @return - */ - String name() default ""; -} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/FormClass.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/FormClass.java b/common/src/main/java/org/apache/sqoop/model/FormClass.java deleted file mode 100644 index 6048d03..0000000 --- a/common/src/main/java/org/apache/sqoop/model/FormClass.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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.sqoop.model; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Denote configuration class - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface FormClass { - - /** - * Default size for Inputs in this form. - * - * @return - */ - short defaultSize() default -1; - - /** - * List of validators associated with this form. - * - * @return - */ - Validator[] validators() default {}; -} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/FormUtils.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/FormUtils.java b/common/src/main/java/org/apache/sqoop/model/FormUtils.java deleted file mode 100644 index 586e0fe..0000000 --- a/common/src/main/java/org/apache/sqoop/model/FormUtils.java +++ /dev/null @@ -1,634 +0,0 @@ -/** - * 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.sqoop.model; - -import org.apache.commons.lang.StringUtils; -import org.apache.sqoop.common.SqoopException; -import org.apache.sqoop.utils.ClassUtils; -import org.apache.sqoop.validation.Message; -import org.apache.sqoop.validation.Status; -import org.apache.sqoop.validation.Validation; -import org.apache.sqoop.validation.ValidationResult; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - - -/** - * Util class for transforming data from correctly annotated configuration - * objects to different structures and vice-versa. - * - * TODO: This class should see some overhaul into more reusable code, especially expose and re-use the methods at the end. - */ -public class FormUtils { - - /** - * Transform correctly annotated configuration object to corresponding - * list of forms. - * - * Forms will be order according to the occurrence in the configuration - * class. Inputs will be also ordered based on occurrence. - * - * @param configuration Annotated arbitrary configuration object - * @return Corresponding list of forms - */ - public static List<MForm> toForms(Object configuration) { - return toForms(configuration.getClass(), configuration); - } - - public static List<MForm> toForms(Class klass) { - return toForms(klass, null); - } - - @SuppressWarnings("unchecked") - public static List<MForm> toForms(Class klass, Object configuration) { - - Set<String> formNames = new HashSet<String>(); - - ConfigurationClass global = - (ConfigurationClass)klass.getAnnotation(ConfigurationClass.class); - - // Each configuration object must have this class annotation - if(global == null) { - throw new SqoopException(ModelError.MODEL_003, - "Missing annotation ConfigurationClass on class " + klass.getName()); - } - - List<MForm> forms = new LinkedList<MForm>(); - - // Iterate over all declared fields - for (Field formField : klass.getDeclaredFields()) { - formField.setAccessible(true); - - // Each field that should be part of user input should have Input - // annotation. - Form formAnnotation = formField.getAnnotation(Form.class); - - if (formAnnotation != null) { - String formName = getFormName(formField, formAnnotation, formNames); - - Class type = formField.getType(); - - Object value = null; - if(configuration != null) { - try { - value = formField.get(configuration); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, - "Can't retrieve value from " + formField.getName(), e); - } - } - - forms.add(toForm(formName, type, value)); - } - } - - return forms; - } - - @SuppressWarnings("unchecked") - private static MForm toForm(String formName, Class<?> klass, Object object) { - FormClass global = - (FormClass)klass.getAnnotation(FormClass.class); - - // Each configuration object must have this class annotation - if(global == null) { - throw new SqoopException(ModelError.MODEL_003, - "Missing annotation FormClass on class " + klass.getName()); - } - - // Intermediate list of inputs - List<MInput<?>> inputs = new LinkedList<MInput<?>>(); - - // Iterate over all declared fields - for (Field field : klass.getDeclaredFields()) { - field.setAccessible(true); - - String fieldName = field.getName(); - String inputName = formName + "." + fieldName; - - // Each field that should be part of user input should have Input - // annotation. - Input inputAnnotation = field.getAnnotation(Input.class); - - if(inputAnnotation != null) { - boolean sensitive = inputAnnotation.sensitive(); - short maxLen = inputAnnotation.size(); - Class<?> type = field.getType(); - - MInput input; - - // We need to support NULL, so we do not support primitive types - if(type.isPrimitive()) { - throw new SqoopException(ModelError.MODEL_007, - "Detected primitive type " + type + " for field " + fieldName); - } - - // Instantiate corresponding MInput<?> structure - if (type == String.class) { - input = new MStringInput(inputName, sensitive, maxLen); - } else if (type.isAssignableFrom(Map.class)) { - input = new MMapInput(inputName, sensitive); - } else if (type == Integer.class) { - input = new MIntegerInput(inputName, sensitive); - } else if (type == Boolean.class) { - input = new MBooleanInput(inputName, sensitive); - } else if (type.isEnum()) { - input = new MEnumInput(inputName, sensitive, - ClassUtils.getEnumStrings(type)); - } else { - throw new SqoopException(ModelError.MODEL_004, "Unsupported type " - + type.getName() + " for input " + fieldName); - } - - // Move value if it's present in original configuration object - if(object != null) { - Object value; - try { - value = field.get(object); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, - "Can't retrieve value from " + field.getName(), e); - } - if(value == null) { - input.setEmpty(); - } else { - input.setValue(value); - } - } - - inputs.add(input); - } - } - - return new MForm(formName, inputs); - } - - private static Field getFieldFromName(Class<?> klass, String name) { - Field formField; - try { - formField = klass.getDeclaredField(name); - } catch (NoSuchFieldException e) { - // reverse lookup form field from custom form name - if (name != null) { - for (Field field : klass.getDeclaredFields()) { - Form formAnnotation = field.getAnnotation(Form.class); - if (formAnnotation == null) { - continue; - } - if (!StringUtils.isEmpty(formAnnotation.name()) && name.equals(formAnnotation.name())) { - return field; - } - } - } - throw new SqoopException(ModelError.MODEL_006, "Missing field " + name + " on form class " - + klass.getCanonicalName(), e); - } - return formField; - } - - /** - * Move form values from form list into corresponding configuration object. - * - * @param forms - * Input form list - * @param configuration - * Output configuration object - */ - public static void fromForms(List<MForm> forms, Object configuration) { - Class<?> klass = configuration.getClass(); - - for (MForm form : forms) { - Field formField = getFieldFromName(klass, form.getName()); - // We need to access this field even if it would be declared as private - formField.setAccessible(true); - Class<?> formClass = formField.getType(); - Object newValue = ClassUtils.instantiate(formClass); - - if (newValue == null) { - throw new SqoopException(ModelError.MODEL_006, - "Can't instantiate new form " + formClass); - } - - for (MInput input : form.getInputs()) { - String[] splitNames = input.getName().split("\\."); - if (splitNames.length != 2) { - throw new SqoopException(ModelError.MODEL_009, "Invalid name: " - + input.getName()); - } - - String inputName = splitNames[1]; - // TODO(jarcec): Names structures fix, handle error cases - Field inputField; - try { - inputField = formClass.getDeclaredField(inputName); - } catch (NoSuchFieldException e) { - throw new SqoopException(ModelError.MODEL_006, "Missing field " - + input.getName(), e); - } - - // We need to access this field even if it would be declared as private - inputField.setAccessible(true); - - try { - if (input.isEmpty()) { - inputField.set(newValue, null); - } else { - if (input.getType() == MInputType.ENUM) { - inputField.set(newValue, Enum.valueOf( - (Class<? extends Enum>) inputField.getType(), - (String) input.getValue())); - } else { - inputField.set(newValue, input.getValue()); - } - } - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, "Issue with field " - + inputField.getName(), e); - } - } - - try { - formField.set(configuration, newValue); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, "Issue with field " - + formField.getName(), e); - } - } - } - - /** - * Apply validations on the forms. - * - * @param forms - * Forms that should be updated - * @param validation - * Validation that we should apply - */ - public static void applyValidation(List<MForm> forms, Validation validation) { - Map<Validation.FormInput, Validation.Message> messages = validation - .getMessages(); - - for (MForm form : forms) { - applyValidation(form, messages); - - for (MInput input : form.getInputs()) { - applyValidation(input, messages); - } - } - } - - /** - * Apply validation on given validated element. - * - * @param element - * Element on what we're applying the validations - * @param messages - * Map of all validation messages - */ - public static void applyValidation(MValidatedElement element, - Map<Validation.FormInput, Validation.Message> messages) { - Validation.FormInput name = new Validation.FormInput(element.getName()); - - if (messages.containsKey(name)) { - Validation.Message message = messages.get(name); - element.addValidationMessage(new Message(message.getStatus(), message.getMessage())); - } else { - element.addValidationMessage(new Message(Status.getDefault(), null)); - } - } - - - /** - * Apply given validations on list of forms. - * - * @param forms - * @param result - */ - public static void applyValidation(List<MForm> forms, ValidationResult result) { - for(MForm form : forms) { - applyValidation(form, result); - - for(MInput input : form.getInputs()) { - applyValidation(input, result); - } - } - } - - /** - * Apply validation messages on given element. - * - * Element's state will be set to default if there are no associated messages. - * - * @param element - * @param result - */ - public static void applyValidation(MValidatedElement element, ValidationResult result) { - List<Message> messages = result.getMessages().get(element.getName()); - - if(messages != null) { - element.setValidationMessages(messages); - } else { - element.resetValidationMessages(); - } - } - - /** - * Convert configuration object to JSON. Only filled properties are serialized, - * properties with null value are skipped. - * - * @param configuration Correctly annotated configuration object - * @return String of JSON representation - */ - @SuppressWarnings("unchecked") - public static String toJson(Object configuration) { - Class klass = configuration.getClass(); - Set<String> formNames = new HashSet<String>(); - ConfigurationClass global = (ConfigurationClass) klass - .getAnnotation(ConfigurationClass.class); - - // Each configuration object must have this class annotation - if (global == null) { - throw new SqoopException(ModelError.MODEL_003, - "Missing annotation Configuration on class " + klass.getName()); - } - - JSONObject jsonOutput = new JSONObject(); - - // Iterate over all declared fields - for (Field formField : klass.getDeclaredFields()) { - formField.setAccessible(true); - - // We're processing only form validations - Form formAnnotation = formField.getAnnotation(Form.class); - if (formAnnotation == null) { - continue; - } - String formName = getFormName(formField, formAnnotation, formNames); - - Object formValue; - try { - formValue = formField.get(configuration); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, "Issue with field " - + formName, e); - } - - JSONObject jsonForm = new JSONObject(); - - // Now process each input on the form - for (Field inputField : formField.getType().getDeclaredFields()) { - inputField.setAccessible(true); - String inputName = inputField.getName(); - - Object value; - try { - value = inputField.get(formValue); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, "Issue with field " - + formName + "." + inputName, e); - } - - Input inputAnnotation = inputField.getAnnotation(Input.class); - - // Do not serialize all values - if (inputAnnotation != null && value != null) { - Class<?> type = inputField.getType(); - - // We need to support NULL, so we do not support primitive types - if (type.isPrimitive()) { - throw new SqoopException(ModelError.MODEL_007, - "Detected primitive type " + type + " for field " + formName - + "." + inputName); - } - - if (type == String.class) { - jsonForm.put(inputName, value); - } else if (type.isAssignableFrom(Map.class)) { - JSONObject map = new JSONObject(); - for (Object key : ((Map) value).keySet()) { - map.put(key, ((Map) value).get(key)); - } - jsonForm.put(inputName, map); - } else if (type == Integer.class) { - jsonForm.put(inputName, value); - } else if (type.isEnum()) { - jsonForm.put(inputName, value.toString()); - } else if (type == Boolean.class) { - jsonForm.put(inputName, value); - } else { - throw new SqoopException(ModelError.MODEL_004, "Unsupported type " - + type.getName() + " for input " + formName + "." + inputName); - } - } - } - jsonOutput.put(formName, jsonForm); - } - return jsonOutput.toJSONString(); - } - - /** - * Parse given input JSON string and move it's values to given configuration - * object. - * - * @param json JSON representation of the configuration object - * @param configuration Configuration object to be filled - */ - public static void fillValues(String json, Object configuration) { - Class klass = configuration.getClass(); - - JSONObject jsonForms = (JSONObject) JSONValue.parse(json); - Set<String> formNames = new HashSet<String>(); - - for(Field formField : klass.getDeclaredFields()) { - formField.setAccessible(true); - - // We're processing only form validations - Form formAnnotation = formField.getAnnotation(Form.class); - if(formAnnotation == null) { - continue; - } - String formName = getFormName(formField, formAnnotation, formNames); - - try { - formField.set(configuration, formField.getType().newInstance()); - } catch (Exception e) { - throw new SqoopException(ModelError.MODEL_005, - "Issue with field " + formName, e); - } - - JSONObject jsonInputs = (JSONObject) jsonForms.get(formName); - if(jsonInputs == null) { - continue; - } - - Object formValue; - try { - formValue = formField.get(configuration); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, - "Issue with field " + formName, e); - } - - for(Field inputField : formField.getType().getDeclaredFields()) { - inputField.setAccessible(true); - String inputName = inputField.getName(); - - Input inputAnnotation = inputField.getAnnotation(Input.class); - - if(inputAnnotation == null || jsonInputs.get(inputName) == null) { - try { - inputField.set(formValue, null); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, - "Issue with field " + formName + "." + inputName, e); - } - continue; - } - - Class type = inputField.getType(); - - try { - if(type == String.class) { - inputField.set(formValue, jsonInputs.get(inputName)); - } else if (type.isAssignableFrom(Map.class)) { - Map<String, String> map = new HashMap<String, String>(); - JSONObject jsonObject = (JSONObject) jsonInputs.get(inputName); - for(Object key : jsonObject.keySet()) { - map.put((String)key, (String)jsonObject.get(key)); - } - inputField.set(formValue, map); - } else if(type == Integer.class) { - inputField.set(formValue, ((Long)jsonInputs.get(inputName)).intValue()); - } else if(type.isEnum()) { - inputField.set(formValue, Enum.valueOf((Class<? extends Enum>) inputField.getType(), (String) jsonInputs.get(inputName))); - } else if(type == Boolean.class) { - inputField.set(formValue, (Boolean) jsonInputs.get(inputName)); - }else { - throw new SqoopException(ModelError.MODEL_004, - "Unsupported type " + type.getName() + " for input " + formName + "." + inputName); - } - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_005, - "Issue with field " + formName + "." + inputName, e); - } - } - } - } - - private static String getFormName(Field member, Form annotation, Set<String> existingFormNames) { - if (StringUtils.isEmpty(annotation.name())) { - return member.getName(); - } else { - checkForValidFormName(existingFormNames, annotation.name()); - existingFormNames.add(annotation.name()); - return annotation.name(); - } - } - - private static void checkForValidFormName(Set<String> existingFormNames, - String customFormName) { - // uniqueness across fields check - if (existingFormNames.contains(customFormName)) { - throw new SqoopException(ModelError.MODEL_012, - "Issue with field form name " + customFormName); - } - - if (!Character.isJavaIdentifierStart(customFormName.toCharArray()[0])) { - throw new SqoopException(ModelError.MODEL_013, - "Issue with field form name " + customFormName); - } - for (Character c : customFormName.toCharArray()) { - if (Character.isJavaIdentifierPart(c)) - continue; - throw new SqoopException(ModelError.MODEL_013, - "Issue with field form name " + customFormName); - } - - if (customFormName.length() > 30) { - throw new SqoopException(ModelError.MODEL_014, - "Issue with field form name " + customFormName); - - } - } - - public static String getName(Field input, Input annotation) { - return input.getName(); - } - - public static String getName(Field form, Form annotation) { - return form.getName(); - } - - public static ConfigurationClass getConfigurationClassAnnotation(Object object, boolean strict) { - ConfigurationClass annotation = object.getClass().getAnnotation(ConfigurationClass.class); - - if(strict && annotation == null) { - throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationClass on class " + object.getClass().getName()); - } - - return annotation; - } - - public static FormClass getFormClassAnnotation(Object object, boolean strict) { - FormClass annotation = object.getClass().getAnnotation(FormClass.class); - - if(strict && annotation == null) { - throw new SqoopException(ModelError.MODEL_003, "Missing annotation ConfigurationClass on class " + object.getClass().getName()); - } - - return annotation; - } - - public static Form getFormAnnotation(Field field, boolean strict) { - Form annotation = field.getAnnotation(Form.class); - - if(strict && annotation == null) { - throw new SqoopException(ModelError.MODEL_003, "Missing annotation Form on Field " + field.getName() + " on class " + field.getDeclaringClass().getName()); - } - - return annotation; - } - - public static Input getInputAnnotation(Field field, boolean strict) { - Input annotation = field.getAnnotation(Input.class); - - if(strict && annotation == null) { - throw new SqoopException(ModelError.MODEL_003, "Missing annotation Input on Field " + field.getName() + " on class " + field.getDeclaringClass().getName()); - } - - return annotation; - } - - public static Object getFieldValue(Field field, Object object) { - try { - field.setAccessible(true); - return field.get(object); - } catch (IllegalAccessException e) { - throw new SqoopException(ModelError.MODEL_015, e); - } - } - -} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/MConfig.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/MConfig.java b/common/src/main/java/org/apache/sqoop/model/MConfig.java new file mode 100644 index 0000000..b5d2afd --- /dev/null +++ b/common/src/main/java/org/apache/sqoop/model/MConfig.java @@ -0,0 +1,117 @@ +/** + * 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.sqoop.model; + +import org.apache.sqoop.common.SqoopException; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a group of inputs that are processed together. This allows the + * input gathering process to be broken down into multiple steps that can be + * then paged through by the user interface. + */ +public final class MConfig extends MValidatedElement implements MClonable { + + private final List<MInput<?>> inputs; + + public MConfig(String name, List<MInput<?>> inputs) { + super(name); + + this.inputs = inputs; + } + + public List<MInput<?>> getInputs() { + return inputs; + } + + public MInput<?> getInput(String inputName) { + for(MInput<?> input: inputs) { + if(inputName.equals(input.getName())) { + return input; + } + } + + throw new SqoopException(ModelError.MODEL_011, "Input name: " + inputName); + } + + public MStringInput getStringInput(String inputName) { + return (MStringInput)getInput(inputName); + } + + public MEnumInput getEnumInput(String inputName) { + return (MEnumInput)getInput(inputName); + } + + public MIntegerInput getIntegerInput(String inputName) { + return (MIntegerInput)getInput(inputName); + } + + public MBooleanInput getBooleanInput(String inputName) { + return (MBooleanInput)getInput(inputName); + } + + public MMapInput getMapInput(String inputName) { + return (MMapInput)getInput(inputName); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("config-").append(getName()); + sb.append(":").append(getPersistenceId()).append(":").append(inputs); + + return sb.toString(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof MConfig)) { + return false; + } + + MConfig mf = (MConfig) other; + return getName().equals(mf.getName()) + && inputs.equals(mf.inputs); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + getName().hashCode(); + for (MInput<?> mi : inputs) { + result = 31 * result + mi.hashCode(); + } + + return result; + } + + @Override + public MConfig clone(boolean cloneWithValue) { + List<MInput<?>> copyInputs = new ArrayList<MInput<?>>(); + for(MInput<?> itr : this.getInputs()) { + copyInputs.add((MInput<?>)itr.clone(cloneWithValue)); + } + MConfig copyConfig = new MConfig(this.getName(), copyInputs); + return copyConfig; + } +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/MConfigList.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/MConfigList.java b/common/src/main/java/org/apache/sqoop/model/MConfigList.java new file mode 100644 index 0000000..8747b55 --- /dev/null +++ b/common/src/main/java/org/apache/sqoop/model/MConfigList.java @@ -0,0 +1,124 @@ +/** + * 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.sqoop.model; + +import org.apache.sqoop.common.SqoopException; + +import java.util.ArrayList; +import java.util.List; + +/** + * Arbitrary list of config objects. + */ +public class MConfigList implements MClonable { + + private final List<MConfig> configObjects; + + public MConfigList(List<MConfig> configObjects) { + this.configObjects = configObjects; + } + + public List<MConfig> getConfigs() { + return configObjects; + } + + public MConfig getConfig(String configName) { + for(MConfig config: configObjects) { + if(configName.equals(config.getName())) { + return config; + } + } + + throw new SqoopException(ModelError.MODEL_010, "config name: " + configName); + } + + public MInput getInput(String name) { + String []parts = name.split("\\."); + if(parts.length != 2) { + throw new SqoopException(ModelError.MODEL_009, name); + } + + return getConfig(parts[0]).getInput(name); + } + + public MStringInput getStringInput(String name) { + return (MStringInput)getInput(name); + } + + public MEnumInput getEnumInput(String name) { + return (MEnumInput)getInput(name); + } + + public MIntegerInput getIntegerInput(String name) { + return (MIntegerInput)getInput(name); + } + + public MMapInput getMapInput(String name) { + return (MMapInput)getInput(name); + } + + public MBooleanInput getBooleanInput(String name) { + return (MBooleanInput)getInput(name); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MConfigList)) return false; + + MConfigList mConfigList = (MConfigList) o; + + if (!configObjects.equals(mConfigList.configObjects)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + for(MConfig config : configObjects) { + result = 31 * result + config.hashCode(); + } + + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Configs: "); + for(MConfig config : configObjects) { + sb.append(config.toString()); + } + return sb.toString(); + } + + @Override + public MConfigList clone(boolean cloneWithValue) { + List<MConfig> copyConfigs = null; + if(this.getConfigs() != null) { + copyConfigs = new ArrayList<MConfig>(); + for(MConfig itr : this.getConfigs()) { + MConfig newConfig = itr.clone(cloneWithValue); + newConfig.setPersistenceId(itr.getPersistenceId()); + copyConfigs.add(newConfig); + } + } + MConfigList copyConfigList = new MConfigList(copyConfigs); + return copyConfigList; + } +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/MConfigType.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/MConfigType.java b/common/src/main/java/org/apache/sqoop/model/MConfigType.java new file mode 100644 index 0000000..de05332 --- /dev/null +++ b/common/src/main/java/org/apache/sqoop/model/MConfigType.java @@ -0,0 +1,34 @@ +/** + * 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.sqoop.model; + +/** + * Represents the various config types supported by the system. + */ +public enum MConfigType { + + /** Unknown config type */ + OTHER, + + /** link config type */ + LINK, + + /** Job config type */ + JOB; + +} http://git-wip-us.apache.org/repos/asf/sqoop/blob/8362c73c/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java b/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java deleted file mode 100644 index 457ccdb..0000000 --- a/common/src/main/java/org/apache/sqoop/model/MConnectionForms.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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.sqoop.model; - -import java.util.List; - -/** - * Metadata describing all required information to build up an connection - * object for one part. Both connector and framework need to supply this object - * to build up entire connection. - */ -public class MConnectionForms extends MFormList { - - public MConnectionForms(List<MForm> forms) { - super(forms); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("Connection: "); - sb.append(super.toString()); - return sb.toString(); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - return super.equals(other); - } - - @Override - public MConnectionForms clone(boolean cloneWithValue) { - MConnectionForms copy = new MConnectionForms(super.clone(cloneWithValue).getForms()); - return copy; - } -}
