Repository: ambari Updated Branches: refs/heads/trunk 252e94c63 -> e2757784c
AMBARI-8295 - Views: support validation (tbeerbower) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e2757784 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e2757784 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e2757784 Branch: refs/heads/trunk Commit: e2757784ca251c18ecdd248a7a1a8e4a3019fac3 Parents: 252e94c Author: tbeerbower <tbeerbo...@hortonworks.com> Authored: Wed Nov 12 15:21:40 2014 -0500 Committer: tbeerbower <tbeerbo...@hortonworks.com> Committed: Wed Nov 12 15:22:10 2014 -0500 ---------------------------------------------------------------------- .../controller/internal/BaseProvider.java | 69 ++++++++++-- .../internal/ViewInstanceResourceProvider.java | 23 ++++ .../ambari/server/orm/entities/ViewEntity.java | 34 ++++++ .../server/orm/entities/ViewInstanceEntity.java | 73 ++++++++++--- .../apache/ambari/server/view/ViewRegistry.java | 54 +++++++--- .../server/view/configuration/ViewConfig.java | 37 +++++++ .../InstanceValidationResultImpl.java | 106 +++++++++++++++++++ .../view/validation/ValidationResultImpl.java | 80 ++++++++++++++ .../controller/internal/BaseProviderTest.java | 28 +++++ .../server/orm/entities/ViewEntityTest.java | 27 +++++ .../orm/entities/ViewInstanceEntityTest.java | 79 +++++++++++++- .../view/configuration/ViewConfigTest.java | 24 +++++ .../InstanceValidationResultImplTest.java | 66 ++++++++++++ .../validation/ValidationResultImplTest.java | 55 ++++++++++ .../view/validation/ValidationResult.java | 54 ++++++++++ .../ambari/view/validation/Validator.java | 59 +++++++++++ ambari-views/src/main/resources/view.xsd | 5 + 17 files changed, 835 insertions(+), 38 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java index b4d582a..a95342a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseProvider.java @@ -279,6 +279,21 @@ public abstract class BaseProvider { } /** + * Determine whether or not the given property id is part the given set of requested ids. This + * accounts for the cases where the given properties are actually property categories. + * + * @param propertyId the property id + * @param requestedIds the requested set of property ids + * + * @return true if the given property id is part of the given set of requested ids + */ + protected static boolean isPropertyRequested(String propertyId, Set<String> requestedIds) { + return requestedIds.contains(propertyId) || + isPropertyCategoryRequested(propertyId, requestedIds) || + isPropertyEntryRequested(propertyId, requestedIds); + } + + /** * Set a property value on the given resource for the given id and value. * Make sure that the id is in the given set of requested ids. * @@ -287,16 +302,9 @@ public abstract class BaseProvider { * @param value the value to set * @param requestedIds the requested set of property ids */ - protected static boolean setResourceProperty(Resource resource, String propertyId, Object value, Set<String> requestedIds) { - boolean contains = requestedIds.contains(propertyId); - - if (!contains) { - String category = PropertyHelper.getPropertyCategory(propertyId); - while (category != null && !contains) { - contains = requestedIds.contains(category); - category = PropertyHelper.getPropertyCategory(category); - } - } + protected static boolean setResourceProperty(Resource resource, String propertyId, Object value, + Set<String> requestedIds) { + boolean contains = requestedIds.contains(propertyId) || isPropertyCategoryRequested(propertyId, requestedIds); if (contains) { if (LOG.isDebugEnabled()) { @@ -364,6 +372,47 @@ public abstract class BaseProvider { return false; } + /** + * Determine whether or not any of the requested ids are an entry for the given property, if + * the given property is a category. For example, if the given property is 'category/subcategory' + * and the set of requested ids contains 'category/subcategory/property' then this method should + * return true. + * + * @param propertyId the property id + * @param requestedIds the requested set of property ids + * + * @return true if the given property is a category for any of the requested ids + */ + private static boolean isPropertyEntryRequested(String propertyId, Set<String> requestedIds) { + for (String requestedId : requestedIds) { + if (requestedId.startsWith(propertyId)) { + return true; + } + } + return false; + } + + /** + * Determine whether or not any of the requested ids are a category for the given property. + * For example, if the given property is 'category/subcategory/property' and the set of requested ids + * contains 'category' or 'category/subcategory' then this method should return true. + * + * @param propertyId the property id + * @param requestedIds the requested set of property ids + * + * @return true if the given property's category is part of the given set of requested ids + */ + private static boolean isPropertyCategoryRequested(String propertyId, Set<String> requestedIds) { + String category = PropertyHelper.getPropertyCategory(propertyId); + while (category != null ) { + if (requestedIds.contains(category)) { + return true; + } + category = PropertyHelper.getPropertyCategory(category); + } + return false; + } + // ----- accessors --------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java index 3f62cc3..a944e95 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewInstanceResourceProvider.java @@ -36,6 +36,9 @@ import org.apache.ambari.server.orm.entities.ViewInstanceEntity; import org.apache.ambari.server.orm.entities.ViewInstancePropertyEntity; import org.apache.ambari.server.orm.entities.ViewParameterEntity; import org.apache.ambari.server.view.ViewRegistry; +import org.apache.ambari.server.view.validation.InstanceValidationResultImpl; +import org.apache.ambari.server.view.validation.ValidationResultImpl; +import org.apache.ambari.view.validation.Validator; import java.util.Collections; import java.util.HashMap; @@ -65,6 +68,10 @@ public class ViewInstanceResourceProvider extends AbstractResourceProvider { public static final String CONTEXT_PATH_PROPERTY_ID = "ViewInstanceInfo/context_path"; public static final String STATIC_PROPERTY_ID = "ViewInstanceInfo/static"; + // validation properties + public static final String VALIDATION_RESULT_PROPERTY_ID = "ViewInstanceInfo/validation_result"; + public static final String PROPERTY_VALIDATION_RESULTS_PROPERTY_ID = "ViewInstanceInfo/property_validation_results"; + /** * Property prefix values. */ @@ -98,6 +105,8 @@ public class ViewInstanceResourceProvider extends AbstractResourceProvider { propertyIds.add(DATA_PROPERTY_ID); propertyIds.add(CONTEXT_PATH_PROPERTY_ID); propertyIds.add(STATIC_PROPERTY_ID); + propertyIds.add(VALIDATION_RESULT_PROPERTY_ID); + propertyIds.add(PROPERTY_VALIDATION_RESULTS_PROPERTY_ID); } // ----- Constructors ------------------------------------------------------ @@ -255,6 +264,20 @@ public class ViewInstanceResourceProvider extends AbstractResourceProvider { setResourceProperty(resource, ICON_PATH_ID, getIconPath(contextPath, viewInstanceEntity.getIcon()), requestedIds); setResourceProperty(resource, ICON64_PATH_ID, getIconPath(contextPath, viewInstanceEntity.getIcon64()), requestedIds); + // if the view provides its own validator then run it + if (viewEntity.hasValidator()) { + + if (isPropertyRequested(VALIDATION_RESULT_PROPERTY_ID, requestedIds) || + isPropertyRequested(PROPERTY_VALIDATION_RESULTS_PROPERTY_ID, requestedIds)) { + + InstanceValidationResultImpl result = + viewInstanceEntity.getValidationResult(viewEntity, Validator.ValidationContext.EXISTING); + + setResourceProperty(resource, VALIDATION_RESULT_PROPERTY_ID, ValidationResultImpl.create(result), requestedIds); + setResourceProperty(resource, PROPERTY_VALIDATION_RESULTS_PROPERTY_ID, result.getPropertyResults(), requestedIds); + } + } + return resource; } http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java index d42e1a0..2f83e02 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java @@ -24,6 +24,7 @@ import org.apache.ambari.server.controller.spi.ResourceProvider; import org.apache.ambari.server.view.ViewSubResourceDefinition; import org.apache.ambari.server.view.configuration.ResourceConfig; import org.apache.ambari.server.view.configuration.ViewConfig; +import org.apache.ambari.view.validation.Validator; import org.apache.ambari.view.View; import org.apache.ambari.view.ViewDefinition; @@ -211,6 +212,12 @@ public class ViewEntity implements ViewDefinition { private View view = null; /** + * The view validator. + */ + @Transient + private Validator validator = null; + + /** * The view status. */ @Transient @@ -705,6 +712,33 @@ public class ViewEntity implements ViewDefinition { } /** + * Set the view validator. + * + * @param validator the view validator + */ + public void setValidator(Validator validator) { + this.validator = validator; + } + + /** + * Get the associated view validator. + * + * @return the view validator + */ + public Validator getValidator() { + return validator; + } + + /** + * Determine whether or not a validator has been specified for this view. + * + * @return true if this view has a validator + */ + public boolean hasValidator() { + return validator != null; + } + + /** * Set the mask class name. * * @param mask the mask class name http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java index efa2818..1dd1b5a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java @@ -48,9 +48,13 @@ import org.apache.ambari.server.security.SecurityHelper; import org.apache.ambari.server.security.SecurityHelperImpl; import org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter; import org.apache.ambari.server.view.configuration.InstanceConfig; +import org.apache.ambari.server.view.validation.InstanceValidationResultImpl; +import org.apache.ambari.server.view.validation.ValidationResultImpl; +import org.apache.ambari.view.validation.Validator; import org.apache.ambari.view.ResourceProvider; import org.apache.ambari.view.ViewDefinition; import org.apache.ambari.view.ViewInstanceDefinition; +import org.apache.ambari.view.validation.ValidationResult; /** * Represents an instance of a View. @@ -158,7 +162,7 @@ public class ViewInstanceEntity implements ViewInstanceDefinition { @OneToOne(cascade = CascadeType.ALL) @JoinColumns({ - @JoinColumn(name = "resource_id", referencedColumnName = "resource_id", nullable = false), + @JoinColumn(name = "resource_id", referencedColumnName = "resource_id", nullable = false) }) private ResourceEntity resource; @@ -712,26 +716,67 @@ public class ViewInstanceEntity implements ViewInstanceDefinition { * Validate the state of the instance. * * @param viewEntity the view entity to which this instance will be bound + * @param context the validation context + * * @throws IllegalStateException if the instance is not in a valid state */ - public void validate(ViewEntity viewEntity) throws IllegalStateException { + public void validate(ViewEntity viewEntity, Validator.ValidationContext context) throws IllegalStateException { + InstanceValidationResultImpl result = getValidationResult(viewEntity, context); + if (!result.isValid()) { + throw new IllegalStateException(result.toJson()); + } + } - // make sure that there is an instance property value defined - // for each required view parameter - Set<String> requiredParamterNames = new HashSet<String>(); - for (ViewParameterEntity parameter : viewEntity.getParameters()) { - if (parameter.isRequired()) { - requiredParamterNames.add(parameter.getName()); + /** + * Get the validation the state of the instance. + * + * @param viewEntity the view entity to which this instance will be bound + * @param context the validation context + * + * @return the instance validation result + */ + public InstanceValidationResultImpl getValidationResult(ViewEntity viewEntity, Validator.ValidationContext context) + throws IllegalStateException { + + Map<String, ValidationResult> propertyResults = new HashMap<String, ValidationResult>(); + + if (context.equals(Validator.ValidationContext.PRE_CREATE) || + context.equals(Validator.ValidationContext.PRE_UPDATE)) { + + // make sure that there is an instance property value defined + // for each required view parameter + Set<String> requiredParameterNames = new HashSet<String>(); + for (ViewParameterEntity parameter : viewEntity.getParameters()) { + if (parameter.isRequired()) { + requiredParameterNames.add(parameter.getName()); + } + } + Collection<ViewInstancePropertyEntity> propertyEntities = getProperties(); + for (ViewInstancePropertyEntity property : propertyEntities) { + requiredParameterNames.remove(property.getName()); + } + // required but missing instance properties... + for (String requiredParameterName : requiredParameterNames) { + propertyResults.put(requiredParameterName, + new ValidationResultImpl(false, + "No property values exist for the required parameter " + requiredParameterName + ".")); } - } - for (ViewInstancePropertyEntity property : getProperties()) { - requiredParamterNames.remove(property.getName()); } - if (!requiredParamterNames.isEmpty()) { - throw new IllegalStateException("No property values exist for the required parameters " + - requiredParamterNames); + ValidationResult instanceResult = null; + Validator validator = viewEntity.getValidator(); + + // if the view provides its own validator, run it + if (validator != null) { + instanceResult = validator.validateInstance(this, context); + for ( String property : getPropertyMap().keySet()) { + if (!propertyResults.containsKey(property)) { + propertyResults.put(property, + ValidationResultImpl.create(validator.validateProperty(property, this, context))); + } + } } + return new InstanceValidationResultImpl(ValidationResultImpl.create(instanceResult), propertyResults); } http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java index a5d11bd..0dbf32c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java @@ -22,6 +22,7 @@ import com.google.common.collect.Sets; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; +import com.google.inject.persist.Transactional; import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl; import org.apache.ambari.server.api.resources.SubResourceDefinition; import org.apache.ambari.server.api.resources.ViewExternalSubResourceDefinition; @@ -64,6 +65,7 @@ import org.apache.ambari.server.view.configuration.PersistenceConfig; import org.apache.ambari.server.view.configuration.PropertyConfig; import org.apache.ambari.server.view.configuration.ResourceConfig; import org.apache.ambari.server.view.configuration.ViewConfig; +import org.apache.ambari.view.validation.Validator; import org.apache.ambari.view.Masker; import org.apache.ambari.view.SystemException; import org.apache.ambari.view.View; @@ -461,6 +463,7 @@ public class ViewRegistry { * does not exist * @throws SystemException if the instance can not be installed */ + @Transactional public void installViewInstance(ViewInstanceEntity instanceEntity) throws IllegalStateException, IllegalArgumentException, SystemException { ViewEntity viewEntity = getDefinition(instanceEntity.getViewName()); @@ -476,7 +479,7 @@ public class ViewRegistry { version + "/" + instanceName); } - instanceEntity.validate(viewEntity); + instanceEntity.validate(viewEntity, Validator.ValidationContext.PRE_CREATE); ResourceTypeEntity resourceTypeEntity = resourceTypeDAO.findByName(ViewEntity.getViewName(viewName, version)); // create an admin resource to represent this view instance @@ -530,7 +533,7 @@ public class ViewRegistry { ViewEntity viewEntity = getDefinition(instanceEntity.getViewName()); if (viewEntity != null) { - instanceEntity.validate(viewEntity); + instanceEntity.validate(viewEntity, Validator.ValidationContext.PRE_UPDATE); instanceDAO.merge(instanceEntity); } } @@ -541,6 +544,7 @@ public class ViewRegistry { * @param instanceEntity the view instance entity * @throws IllegalStateException if the given instance is not in a valid state */ + @Transactional public void uninstallViewInstance(ViewInstanceEntity instanceEntity) throws IllegalStateException { ViewEntity viewEntity = getDefinition(instanceEntity.getViewName()); @@ -577,6 +581,7 @@ public class ViewRegistry { * @param instanceEntity the instance entity * @param key the data key */ + @Transactional public void removeInstanceData(ViewInstanceEntity instanceEntity, String key) { ViewInstanceDataEntity dataEntity = instanceEntity.getInstanceData(key); if (dataEntity != null) { @@ -903,6 +908,11 @@ public class ViewRegistry { view = getView(viewConfig.getViewClass(cl), new ViewContextImpl(viewDefinition, this)); } viewDefinition.setView(view); + Validator validator = null; + if (viewConfig.getValidator() != null) { + validator = getValidator(viewConfig.getValidatorClass(cl), new ViewContextImpl(viewDefinition, this)); + } + viewDefinition.setValidator(validator); viewDefinition.setMask(viewConfig.getMasker()); Set<SubResourceDefinition> subResourceDefinitions = new HashSet<SubResourceDefinition>(); @@ -927,7 +937,7 @@ public class ViewRegistry { properties.put(propertyConfig.getKey(), propertyConfig.getValue()); } setViewInstanceProperties(viewInstanceDefinition, properties, viewConfig, viewDefinition.getClassLoader()); - viewInstanceDefinition.validate(viewDefinition); + viewInstanceDefinition.validate(viewDefinition, Validator.ValidationContext.PRE_CREATE); bindViewInstance(viewDefinition, viewInstanceDefinition); return viewInstanceDefinition; @@ -1042,6 +1052,19 @@ public class ViewRegistry { return viewInstanceInjector.getInstance(clazz); } + // get the given view validator class from the given class loader; inject a context + private static Validator getValidator(Class<? extends Validator> clazz, + final ViewContext viewContext) { + Injector viewInstanceInjector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(ViewContext.class) + .toInstance(viewContext); + } + }); + return viewInstanceInjector.getInstance(clazz); + } + // create masker from given class; probably replace with injector later private static Masker getMasker(Class<? extends Masker> clazz) { try { @@ -1302,16 +1325,8 @@ public class ViewRegistry { instanceEntity.setXmlDriven(true); instanceDefinitions.add(instanceEntity); } - // ensure that the view entity matches the db - syncView(viewDefinition, instanceDefinitions); - - onDeploy(viewDefinition); + persistView(viewDefinition, instanceDefinitions); - // update the registry with the view instances - for (ViewInstanceEntity instanceEntity : instanceDefinitions) { - addInstanceDefinition(viewDefinition, instanceEntity); - handlerList.addViewInstance(instanceEntity); - } setViewStatus(viewDefinition, ViewEntity.ViewStatus.DEPLOYED, "Deployed " + extractedArchiveDirPath + "."); } catch (Exception e) { @@ -1322,6 +1337,21 @@ public class ViewRegistry { } } + // persist the given view and its instances + @Transactional + private void persistView(ViewEntity viewDefinition, Set<ViewInstanceEntity> instanceDefinitions) throws Exception { + // ensure that the view entity matches the db + syncView(viewDefinition, instanceDefinitions); + + onDeploy(viewDefinition); + + // update the registry with the view instances + for (ViewInstanceEntity instanceEntity : instanceDefinitions) { + addInstanceDefinition(viewDefinition, instanceEntity); + handlerList.addViewInstance(instanceEntity); + } + } + // extract the view archive for the given path. protected static boolean extractViewArchive(String archivePath, ViewExtractor extractor, http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java index 3a23ea7..ea04a21 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java @@ -19,6 +19,7 @@ package org.apache.ambari.server.view.configuration; import org.apache.ambari.server.view.DefaultMasker; +import org.apache.ambari.view.validation.Validator; import org.apache.ambari.view.Masker; import org.apache.ambari.view.View; import org.apache.commons.lang.StringUtils; @@ -84,6 +85,17 @@ public class ViewConfig { private Class<? extends View> viewClass = null; /** + * The main view class name. + */ + @XmlElement(name="validator-class") + private String validator; + + /** + * The view validator class. + */ + private Class<? extends Validator> validatorClass = null; + + /** * The masker class name for parameters. */ @XmlElement(name="masker-class") @@ -213,6 +225,31 @@ public class ViewConfig { } /** + * Get the view validator class name. + * + * @return the view validator class name + */ + public String getValidator() { + return validator; + } + + /** + * Get the view validator class. + * + * @param cl the class loader + * + * @return the view validator class + * + * @throws ClassNotFoundException if the class can not be loaded + */ + public Class<? extends Validator> getValidatorClass(ClassLoader cl) throws ClassNotFoundException { + if (validatorClass == null) { + validatorClass = cl.loadClass(validator).asSubclass(Validator.class); + } + return validatorClass; + } + + /** * Get the masker class name. * @return the masker class name */ http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/view/validation/InstanceValidationResultImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/validation/InstanceValidationResultImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/view/validation/InstanceValidationResultImpl.java new file mode 100644 index 0000000..c536ae5 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/validation/InstanceValidationResultImpl.java @@ -0,0 +1,106 @@ +/** + * 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.ambari.server.view.validation; + +import com.google.gson.Gson; +import org.apache.ambari.view.validation.ValidationResult; + +import java.util.Map; + +/** + * View instance validation result. This result includes the validation results + * for the associated view instance and its properties. + */ +public class InstanceValidationResultImpl extends ValidationResultImpl { + + /** + * Static Gson instance. + */ + private static final Gson GSON = new Gson(); + + /** + * The validation results for the associated instance's properties + */ + private final Map<String, ValidationResult> propertyResults; + + + // ----- Constructors ------------------------------------------------------ + + /** + * Construct an instance validation result. + * + * @param instanceResult the results of the instance validation + * @param propertyResults the results of the property validations + */ + public InstanceValidationResultImpl(ValidationResult instanceResult, Map<String, ValidationResult> propertyResults) { + super(isValid(instanceResult, propertyResults), getDetail(instanceResult, propertyResults)); + this.propertyResults = propertyResults; + } + + + // ----- InstanceValidationResultImpl -------------------------------------- + + /** + * Get the validation results for the properties of the associated instance. + * + * @return the property validation results + */ + public Map<String, ValidationResult> getPropertyResults() { + return propertyResults; + } + + /** + * Return this result as a JSON string. + * + * @return this result in JSON format + */ + public String toJson() { + return GSON.toJson(this); + } + + + // ----- helper methods ---------------------------------------------------- + + // determine whether or not the given instance and property results are valid + private static boolean isValid(ValidationResult instanceResult, Map<String, ValidationResult> propertyResults) { + boolean instanceValid = instanceResult.isValid(); + if (instanceValid) { + for (Map.Entry<String, ValidationResult> entry : propertyResults.entrySet()) { + ValidationResult propertyResult = entry.getValue(); + if (propertyResult != null && !propertyResult.isValid()) { + return false; + } + } + } + return instanceValid; + } + + // get a detail message from the given instance and property results + private static String getDetail(ValidationResult instanceResult, Map<String, ValidationResult> propertyResults) { + if (instanceResult.isValid()) { + for (Map.Entry<String, ValidationResult> entry : propertyResults.entrySet()) { + ValidationResult propertyResult = entry.getValue(); + if (propertyResult != null && !propertyResult.isValid()) { + return "The instance has invalid properties."; + } + } + } + return instanceResult.getDetail(); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/main/java/org/apache/ambari/server/view/validation/ValidationResultImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/validation/ValidationResultImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/view/validation/ValidationResultImpl.java new file mode 100644 index 0000000..643eb1b --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/validation/ValidationResultImpl.java @@ -0,0 +1,80 @@ +/** + * 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.ambari.server.view.validation; + +import org.apache.ambari.view.validation.ValidationResult; + +/** + * Simple validation result implementation. + */ +public class ValidationResultImpl implements ValidationResult{ + /** + * Indicates whether or not the result is valid. + */ + private final boolean valid; + + /** + * Detail message for the result. + */ + private final String detail; + + + // ----- Constructors ------------------------------------------------------ + + /** + * Construct a validation result. + * + * @param valid indicates whether or not the result is valid + * @param detail detail message for the result + */ + public ValidationResultImpl(boolean valid, String detail) { + this.valid = valid; + this.detail = detail; + } + + + // ----- ValidationResult -------------------------------------------------- + + @Override + public boolean isValid() { + return valid; + } + + @Override + public String getDetail() { + return detail; + } + + + // ----- helper methods ---------------------------------------------------- + + /** + * Factory method to create a validation result from an existing result (accounts for null). + * + * @param result the validation result; may be null + * + * @return a new validation result + */ + public static ValidationResult create(ValidationResult result) { + if (result == null) { + result = SUCCESS; + } + return new ValidationResultImpl(result.isValid(), result.getDetail()); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BaseProviderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BaseProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BaseProviderTest.java index fc8acd0..380142e 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BaseProviderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BaseProviderTest.java @@ -171,6 +171,34 @@ public class BaseProviderTest { } @Test + public void testIsPropertyRequested() { + Set<String> propertyIds = new HashSet<String>(); + propertyIds.add("p1"); + propertyIds.add("foo"); + propertyIds.add("cat1/foo"); + propertyIds.add("cat2/bar"); + propertyIds.add("cat2/baz"); + propertyIds.add("cat3/sub1/bam"); + propertyIds.add("cat4/sub2/sub3/bat"); + propertyIds.add("cat5/sub5"); + + assertTrue(BaseProvider.isPropertyRequested("foo", propertyIds)); + + assertTrue(BaseProvider.isPropertyRequested("cat2", propertyIds)); + + assertTrue(BaseProvider.isPropertyRequested("cat2/bar", propertyIds)); + + assertFalse(BaseProvider.isPropertyRequested("unsupported", propertyIds)); + + // we should allow anything under the category cat5/sub5 + assertTrue(BaseProvider.isPropertyRequested("cat5/sub5/prop5", propertyIds)); + assertTrue(BaseProvider.isPropertyRequested("cat5/sub5/sub5a/prop5a", propertyIds)); + + // we shouldn't allow anything under the category cat5/sub7 + assertFalse(BaseProvider.isPropertyRequested("cat5/sub7/unsupported", propertyIds)); + } + + @Test public void testSetResourcePropertyWithMaps() { Set<String> propertyIds = new HashSet<String>(); propertyIds.add("cat1/emptyMapProperty"); http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewEntityTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewEntityTest.java index 965cebb..f93403d 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewEntityTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewEntityTest.java @@ -26,6 +26,9 @@ import org.apache.ambari.server.view.configuration.ResourceConfigTest; import org.apache.ambari.server.view.configuration.ViewConfig; import org.apache.ambari.server.view.configuration.ViewConfigTest; import org.apache.ambari.view.ViewDefinition; +import org.apache.ambari.view.ViewInstanceDefinition; +import org.apache.ambari.view.validation.ValidationResult; +import org.apache.ambari.view.validation.Validator; import org.junit.Assert; import org.junit.Test; @@ -268,6 +271,16 @@ public class ViewEntityTest { } @Test + public void testGetSetValidator() throws Exception { + ViewEntity viewDefinition = getViewEntity(); + + Validator validator = new TestValidator(); + + viewDefinition.setValidator(validator); + Assert.assertEquals(validator, viewDefinition.getValidator()); + } + + @Test public void testisDeployed() throws Exception { ViewEntity viewDefinition = getViewEntity(); @@ -294,4 +307,18 @@ public class ViewEntityTest { viewDefinition.setSystem(true); Assert.assertTrue(viewDefinition.isSystem()); } + + public static class TestValidator implements Validator { + ValidationResult result; + + @Override + public ValidationResult validateInstance(ViewInstanceDefinition definition, ValidationContext mode) { + return result; + } + + @Override + public ValidationResult validateProperty(String property, ViewInstanceDefinition definition, ValidationContext mode) { + return result; + } + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java index 08aea6e..3aa17d1 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/ViewInstanceEntityTest.java @@ -27,7 +27,11 @@ import org.apache.ambari.server.view.configuration.InstanceConfig; import org.apache.ambari.server.view.configuration.InstanceConfigTest; import org.apache.ambari.server.view.configuration.ViewConfig; import org.apache.ambari.server.view.configuration.ViewConfigTest; +import org.apache.ambari.server.view.validation.InstanceValidationResultImpl; +import org.apache.ambari.server.view.validation.ValidationResultImpl; import org.apache.ambari.view.ResourceProvider; +import org.apache.ambari.view.validation.ValidationResult; +import org.apache.ambari.view.validation.Validator; import org.junit.Assert; import org.junit.Test; import org.springframework.security.core.GrantedAuthority; @@ -405,7 +409,26 @@ public class ViewInstanceEntityTest { ViewEntity viewEntity = ViewRegistryTest.getViewEntity(config, ambariConfig, getClass().getClassLoader(), ""); ViewInstanceEntity viewInstanceEntity = ViewRegistryTest.getViewInstanceEntity(viewEntity, config.getInstances().get(0)); - viewInstanceEntity.validate(viewEntity); + viewInstanceEntity.validate(viewEntity, Validator.ValidationContext.PRE_CREATE); + } + + @Test + public void testValidateWithValidator() throws Exception { + + Properties properties = new Properties(); + properties.put("p1", "v1"); + + Configuration ambariConfig = new Configuration(properties); + + ViewConfig config = ViewConfigTest.getConfig(xml_valid_instance); + ViewEntity viewEntity = ViewRegistryTest.getViewEntity(config, ambariConfig, getClass().getClassLoader(), ""); + ViewInstanceEntity viewInstanceEntity = ViewRegistryTest.getViewInstanceEntity(viewEntity, config.getInstances().get(0)); + + ViewEntityTest.TestValidator validator = new ViewEntityTest.TestValidator(); + validator.result = new ValidationResultImpl(true, "detail"); + viewEntity.setValidator(validator); + + viewInstanceEntity.validate(viewEntity, Validator.ValidationContext.PRE_CREATE); } @Test @@ -421,13 +444,65 @@ public class ViewInstanceEntityTest { ViewInstanceEntity viewInstanceEntity = ViewRegistryTest.getViewInstanceEntity(viewEntity, config.getInstances().get(0)); try { - viewInstanceEntity.validate(viewEntity); + viewInstanceEntity.validate(viewEntity, Validator.ValidationContext.PRE_CREATE); + Assert.fail("Expected an IllegalStateException"); + } catch (IllegalStateException e) { + // expected + } + } + + @Test + public void testValidateWithValidator_fail() throws Exception { + + Properties properties = new Properties(); + properties.put("p1", "v1"); + + Configuration ambariConfig = new Configuration(properties); + + ViewConfig config = ViewConfigTest.getConfig(xml_invalid_instance); + ViewEntity viewEntity = ViewRegistryTest.getViewEntity(config, ambariConfig, getClass().getClassLoader(), ""); + ViewInstanceEntity viewInstanceEntity = ViewRegistryTest.getViewInstanceEntity(viewEntity, config.getInstances().get(0)); + + ViewEntityTest.TestValidator validator = new ViewEntityTest.TestValidator(); + validator.result = new ValidationResultImpl(false, "detail"); + viewEntity.setValidator(validator); + + try { + viewInstanceEntity.validate(viewEntity, Validator.ValidationContext.PRE_CREATE); Assert.fail("Expected an IllegalStateException"); } catch (IllegalStateException e) { // expected } } + @Test + public void testGetValidationResult() throws Exception { + + Properties properties = new Properties(); + properties.put("p1", "v1"); + + Configuration ambariConfig = new Configuration(properties); + + ViewConfig config = ViewConfigTest.getConfig(xml_valid_instance); + ViewEntity viewEntity = ViewRegistryTest.getViewEntity(config, ambariConfig, getClass().getClassLoader(), ""); + ViewInstanceEntity viewInstanceEntity = ViewRegistryTest.getViewInstanceEntity(viewEntity, config.getInstances().get(0)); + + ViewEntityTest.TestValidator validator = new ViewEntityTest.TestValidator(); + validator.result = new ValidationResultImpl(true, "detail"); + viewEntity.setValidator(validator); + + InstanceValidationResultImpl result = viewInstanceEntity.getValidationResult(viewEntity, Validator.ValidationContext.PRE_CREATE); + + Map<String, ValidationResult> propertyResults = result.getPropertyResults(); + + junit.framework.Assert.assertEquals(2, propertyResults.size()); + junit.framework.Assert.assertTrue(propertyResults.containsKey("p1")); + junit.framework.Assert.assertTrue(propertyResults.containsKey("p2")); + + junit.framework.Assert.assertTrue(propertyResults.get("p1").isValid()); + junit.framework.Assert.assertTrue(propertyResults.get("p2").isValid()); + } + public static ViewInstanceEntity getViewInstanceEntity(SecurityHelper securityHelper) throws Exception { ViewInstanceEntity viewInstanceEntity = getViewInstanceEntity(); http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java index f6ec403..04cbe2b 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java @@ -24,6 +24,9 @@ import org.apache.ambari.view.ResourceAlreadyExistsException; import org.apache.ambari.view.ResourceProvider; import org.apache.ambari.view.SystemException; import org.apache.ambari.view.UnsupportedPropertyException; +import org.apache.ambari.view.ViewInstanceDefinition; +import org.apache.ambari.view.validation.ValidationResult; +import org.apache.ambari.view.validation.Validator; import org.junit.Assert; import org.junit.Test; @@ -50,6 +53,7 @@ public class ViewConfigTest { " <system>true</system>\n" + " <icon64>/this/is/the/icon/url/icon64.png</icon64>\n" + " <icon>/this/is/the/icon/url/icon.png</icon>\n" + + " <validator-class>org.apache.ambari.server.view.configuration.ViewConfigTest$MyValidator</validator-class>" + " <masker-class>org.apache.ambari.server.view.DefaultMasker</masker-class>" + " <parameter>\n" + " <name>p1</name>\n" + @@ -169,6 +173,12 @@ public class ViewConfigTest { } @Test + public void testGetValidator() throws Exception { + ViewConfig config = getConfig(); + Assert.assertEquals("org.apache.ambari.server.view.configuration.ViewConfigTest$MyValidator", config.getValidator()); + } + + @Test public void testMasker() throws Exception { ViewConfig config = getConfig(); Assert.assertEquals("org.apache.ambari.server.view.DefaultMasker", config.getMasker()); @@ -249,6 +259,20 @@ public class ViewConfigTest { // nothing } + public static class MyValidator implements Validator { + ValidationResult result; + + @Override + public ValidationResult validateInstance(ViewInstanceDefinition definition, ValidationContext mode) { + return result; + } + + @Override + public ValidationResult validateProperty(String property, ViewInstanceDefinition definition, ValidationContext mode) { + return result; + } + } + public static class MyResource { private String id; http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/test/java/org/apache/ambari/server/view/validation/InstanceValidationResultImplTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/validation/InstanceValidationResultImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/validation/InstanceValidationResultImplTest.java new file mode 100644 index 0000000..b375cd2 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/validation/InstanceValidationResultImplTest.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.ambari.server.view.validation; + +import junit.framework.Assert; +import org.apache.ambari.view.validation.ValidationResult; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +/** + * InstanceValidationResultImpl tests. + */ +public class InstanceValidationResultImplTest { + + @Test + public void testGetPropertyResults() throws Exception { + ValidationResult result = new ValidationResultImpl(true, "detail"); + Map<String, ValidationResult> propertyResults = new HashMap<String, ValidationResult>(); + + propertyResults.put("foo", new ValidationResultImpl(true, "foo detail")); + propertyResults.put("bar", new ValidationResultImpl(false, "bar detail")); + + InstanceValidationResultImpl instanceValidationResult = new InstanceValidationResultImpl(result, propertyResults); + + propertyResults = instanceValidationResult.getPropertyResults(); + + Assert.assertEquals(2, propertyResults.size()); + Assert.assertTrue(propertyResults.containsKey("foo")); + Assert.assertTrue(propertyResults.containsKey("bar")); + + Assert.assertTrue(propertyResults.get("foo").isValid()); + Assert.assertFalse(propertyResults.get("bar").isValid()); + } + + @Test + public void testToJson() throws Exception { + ValidationResult result = new ValidationResultImpl(true, "detail"); + Map<String, ValidationResult> propertyResults = new HashMap<String, ValidationResult>(); + + propertyResults.put("foo", new ValidationResultImpl(true, "foo detail")); + propertyResults.put("bar", new ValidationResultImpl(false, "bar detail")); + + InstanceValidationResultImpl instanceValidationResult = new InstanceValidationResultImpl(result, propertyResults); + + Assert.assertEquals("{\"propertyResults\":{\"foo\":{\"valid\":true,\"detail\":\"foo detail\"},\"bar\":{\"valid\":false,\"detail\":\"bar detail\"}},\"valid\":false,\"detail\":\"The instance has invalid properties.\"}", + instanceValidationResult.toJson()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-server/src/test/java/org/apache/ambari/server/view/validation/ValidationResultImplTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/validation/ValidationResultImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/validation/ValidationResultImplTest.java new file mode 100644 index 0000000..9ad201c --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/validation/ValidationResultImplTest.java @@ -0,0 +1,55 @@ +/** + * 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.ambari.server.view.validation; + +import junit.framework.Assert; +import org.apache.ambari.view.validation.ValidationResult; +import org.junit.Test; + +/** + * ValidationResultImpl tests. + */ +public class ValidationResultImplTest { + + @Test + public void testIsValid() throws Exception { + ValidationResult result = new ValidationResultImpl(true, "detail"); + Assert.assertTrue(result.isValid()); + + result = new ValidationResultImpl(false, "detail"); + Assert.assertFalse(result.isValid()); + } + + @Test + public void testGetDetail() throws Exception { + ValidationResult result = new ValidationResultImpl(true, "detail"); + Assert.assertEquals("detail", result.getDetail()); + } + + @Test + public void testCreate() throws Exception { + ValidationResult result = ValidationResultImpl.create(new ValidationResultImpl(true, "is true")); + Assert.assertTrue(result.isValid()); + Assert.assertEquals("is true", result.getDetail()); + + result = ValidationResultImpl.create(new ValidationResultImpl(false, "is false")); + Assert.assertFalse(result.isValid()); + Assert.assertEquals("is false", result.getDetail()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-views/src/main/java/org/apache/ambari/view/validation/ValidationResult.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/validation/ValidationResult.java b/ambari-views/src/main/java/org/apache/ambari/view/validation/ValidationResult.java new file mode 100644 index 0000000..85ea399 --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/validation/ValidationResult.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.ambari.view.validation; + +/** + * Validation result. + */ +public interface ValidationResult { + + /** + * Determine whether or not the result is valid. + * + * @return true if the result is valid + */ + public boolean isValid(); + + /** + * Get the detail of the validation result. + * + * @return the validation result detail + */ + public String getDetail(); + + /** + * Successful validation result. + */ + public static final ValidationResult SUCCESS = new ValidationResult() { + @Override + public boolean isValid() { + return true; + } + + @Override + public String getDetail() { + return "OK"; + } + }; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-views/src/main/java/org/apache/ambari/view/validation/Validator.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/validation/Validator.java b/ambari-views/src/main/java/org/apache/ambari/view/validation/Validator.java new file mode 100644 index 0000000..ee029a8 --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/validation/Validator.java @@ -0,0 +1,59 @@ +/** + * 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.ambari.view.validation; + +import org.apache.ambari.view.ViewInstanceDefinition; + +/** + * Interface for custom view validation. The validator is used validate a view instance and + * its properties. + */ +public interface Validator { + /** + * Validate the given view instance definition. Return {@code null} to indicate that + * no validation was performed. + * + * @param definition the view instance definition + * @param mode the validation mode + * + * @return the instance validation result; may be {@code null} + */ + public ValidationResult validateInstance(ViewInstanceDefinition definition, ValidationContext mode); + + /** + * Validate a property of the given view instance definition. Return {@code null} to indicate that + * no validation was performed. + * + * @param property the property name + * @param definition the view instance definition + * @param mode the validation mode + * + * @return the instance validation result; may be {@code null} + */ + public ValidationResult validateProperty(String property, ViewInstanceDefinition definition, ValidationContext mode); + + /** + * The context in which a view instance validation check is performed. + */ + public enum ValidationContext { + PRE_CREATE, // validation prior to creation + PRE_UPDATE, // validation prior to update + EXISTING // validation of an existing view instance + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e2757784/ambari-views/src/main/resources/view.xsd ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/resources/view.xsd b/ambari-views/src/main/resources/view.xsd index b95e4ec..a9e5b12 100644 --- a/ambari-views/src/main/resources/view.xsd +++ b/ambari-views/src/main/resources/view.xsd @@ -249,6 +249,11 @@ <xs:documentation>The View class to receive framework events.</xs:documentation> </xs:annotation> </xs:element> + <xs:element type="xs:string" name="validator-class" minOccurs="0" maxOccurs="1"> + <xs:annotation> + <xs:documentation>The Validator class to validate view instances and properties.</xs:documentation> + </xs:annotation> + </xs:element> <xs:element type="xs:string" name="masker-class" minOccurs="0" maxOccurs="1"> <xs:annotation> <xs:documentation>The Masker class for masking view parameters.</xs:documentation>