001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.core.model.constraint; 028 029import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 030import gov.nist.secauto.metaschema.core.metapath.ISequence; 031import gov.nist.secauto.metaschema.core.metapath.MetapathException; 032import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter; 033import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; 034import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level; 035import gov.nist.secauto.metaschema.core.util.ObjectUtils; 036 037import org.apache.logging.log4j.LogBuilder; 038import org.apache.logging.log4j.LogManager; 039import org.apache.logging.log4j.Logger; 040 041import java.util.Comparator; 042import java.util.List; 043import java.util.Objects; 044 045import edu.umd.cs.findbugs.annotations.NonNull; 046import edu.umd.cs.findbugs.annotations.Nullable; 047 048public class LoggingConstraintValidationHandler 049 extends AbstractConstraintValidationHandler { 050 private static final Logger LOGGER = LogManager.getLogger(DefaultConstraintValidator.class); 051 @NonNull 052 private IPathFormatter pathFormatter = IPathFormatter.METAPATH_PATH_FORMATER; 053 054 @Override 055 @NonNull 056 public IPathFormatter getPathFormatter() { 057 return pathFormatter; 058 } 059 060 public void setPathFormatter(@NonNull IPathFormatter pathFormatter) { 061 this.pathFormatter = Objects.requireNonNull(pathFormatter, "pathFormatter"); 062 } 063 064 protected LogBuilder getLogBuilder(@NonNull Level level) { 065 LogBuilder retval; 066 switch (level) { 067 case CRITICAL: 068 retval = LOGGER.atFatal(); 069 break; 070 case ERROR: 071 retval = LOGGER.atError(); 072 break; 073 case WARNING: 074 retval = LOGGER.atWarn(); 075 break; 076 case INFORMATIONAL: 077 retval = LOGGER.atInfo(); 078 break; 079 default: 080 throw new UnsupportedOperationException(String.format("unsupported level '%s'", level)); 081 } 082 return retval; 083 } 084 085 @Override 086 protected String toPath(@NonNull INodeItem nodeItem) { 087 return nodeItem.toPath(getPathFormatter()); 088 } 089 090 protected boolean isLogged(@NonNull Level level) { 091 boolean retval; 092 switch (level) { 093 case CRITICAL: 094 retval = LOGGER.isFatalEnabled(); 095 break; 096 case ERROR: 097 retval = LOGGER.isErrorEnabled(); 098 break; 099 case WARNING: 100 retval = LOGGER.isWarnEnabled(); 101 break; 102 case INFORMATIONAL: 103 retval = LOGGER.isInfoEnabled(); 104 break; 105 default: 106 throw new UnsupportedOperationException(String.format("unsupported level '%s'", level)); 107 } 108 return retval; 109 } 110 111 protected void logConstraint( 112 @NonNull Level level, 113 @NonNull INodeItem node, 114 @NonNull CharSequence message, 115 @Nullable Throwable cause) { 116 LogBuilder builder = getLogBuilder(level); 117 if (cause != null) { 118 builder.withThrowable(cause); 119 } 120 121 builder.log("{}: ({}) {}", level.name(), toPath(node), message); 122 } 123 124 @Override 125 public void handleCardinalityMinimumViolation( 126 @NonNull ICardinalityConstraint constraint, 127 @NonNull INodeItem node, 128 @NonNull ISequence<? extends INodeItem> targets) { 129 Level level = constraint.getLevel(); 130 if (isLogged(level)) { 131 logConstraint(level, node, newCardinalityMinimumViolationMessage(constraint, node, targets), null); 132 } 133 } 134 135 @Override 136 public void handleCardinalityMaximumViolation( 137 @NonNull ICardinalityConstraint constraint, 138 @NonNull INodeItem node, 139 @NonNull ISequence<? extends INodeItem> targets) { 140 Level level = constraint.getLevel(); 141 if (isLogged(level)) { 142 logConstraint(level, node, newCardinalityMaximumViolationMessage(constraint, node, targets), null); 143 } 144 } 145 146 @Override 147 public void handleIndexDuplicateKeyViolation( 148 @NonNull IIndexConstraint constraint, 149 @NonNull INodeItem node, 150 @NonNull INodeItem oldItem, 151 @NonNull INodeItem target) { 152 Level level = constraint.getLevel(); 153 if (isLogged(level)) { 154 logConstraint(level, target, newIndexDuplicateKeyViolationMessage(constraint, node, oldItem, target), null); 155 } 156 } 157 158 @Override 159 public void handleUniqueKeyViolation( 160 @NonNull IUniqueConstraint constraint, 161 @NonNull INodeItem node, 162 @NonNull INodeItem oldItem, 163 @NonNull INodeItem target) { 164 Level level = constraint.getLevel(); 165 if (isLogged(level)) { 166 logConstraint(level, target, newUniqueKeyViolationMessage(constraint, node, oldItem, target), null); 167 } 168 } 169 170 @SuppressWarnings("null") 171 @Override 172 public void handleKeyMatchError( 173 @NonNull IKeyConstraint constraint, 174 @NonNull INodeItem node, 175 @NonNull INodeItem target, 176 @NonNull MetapathException cause) { 177 Level level = constraint.getLevel(); 178 if (isLogged(level)) { 179 logConstraint(level, target, cause.getLocalizedMessage(), cause); 180 } 181 } 182 183 @Override 184 public void handleMatchPatternViolation( 185 @NonNull IMatchesConstraint constraint, 186 @NonNull INodeItem node, 187 @NonNull INodeItem target, 188 @NonNull String value) { 189 Level level = constraint.getLevel(); 190 if (isLogged(level)) { 191 logConstraint(level, target, newMatchPatternViolationMessage(constraint, node, target, value), null); 192 } 193 } 194 195 @Override 196 public void handleMatchDatatypeViolation( 197 @NonNull IMatchesConstraint constraint, 198 @NonNull INodeItem node, 199 @NonNull INodeItem target, 200 @NonNull String value, 201 @NonNull IllegalArgumentException cause) { 202 Level level = constraint.getLevel(); 203 if (isLogged(level)) { 204 logConstraint(level, target, newMatchDatatypeViolationMessage(constraint, node, target, value), cause); 205 } 206 } 207 208 @Override 209 public void handleExpectViolation( 210 @NonNull IExpectConstraint constraint, 211 @NonNull INodeItem node, 212 @NonNull INodeItem target, 213 @NonNull DynamicContext dynamicContext) { 214 Level level = constraint.getLevel(); 215 if (isLogged(level)) { 216 logConstraint(level, target, newExpectViolationMessage(constraint, node, target, dynamicContext), null); 217 } 218 } 219 220 @Override 221 public void handleAllowedValuesViolation(@NonNull List<IAllowedValuesConstraint> failedConstraints, 222 @NonNull INodeItem target) { 223 224 Level level = ObjectUtils.notNull(failedConstraints.stream() 225 .map(IConstraint::getLevel) 226 .max(Comparator.comparing(Level::ordinal)) 227 .get()); 228 if (isLogged(level)) { 229 logConstraint(level, target, newAllowedValuesViolationMessage(failedConstraints, target), null); 230 } 231 } 232 233 @Override 234 public void handleIndexDuplicateViolation(IIndexConstraint constraint, INodeItem node) { 235 Level level = Level.CRITICAL; 236 if (isLogged(level)) { 237 logConstraint(level, node, newIndexDuplicateViolationMessage(constraint, node), null); 238 } 239 } 240 241 @Override 242 public void handleIndexMiss(IIndexHasKeyConstraint constraint, INodeItem node, INodeItem target, List<String> key) { 243 Level level = constraint.getLevel(); 244 if (isLogged(level)) { 245 logConstraint(level, node, newIndexMissMessage(constraint, node, target, key), null); 246 } 247 } 248 249}