blob: f5f781ead0e0403556b6ffba5cf98d656ae53880 [file] [log] [blame]
/*
* 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 com.opensymphony.xwork2.validator;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.Initializable;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Default validator factory
*
* @version $Date$ $Id$
* @author Jason Carreira
* @author James House
*/
public class DefaultValidatorFactory implements ValidatorFactory, Initializable {
protected Map<String, String> validators = new HashMap<>();
private static Logger LOG = LogManager.getLogger(DefaultValidatorFactory.class);
protected ObjectFactory objectFactory;
protected ValidatorFileParser validatorFileParser;
@Inject
public DefaultValidatorFactory(@Inject ObjectFactory objectFactory, @Inject ValidatorFileParser parser) {
this.objectFactory = objectFactory;
this.validatorFileParser = parser;
}
@Override
public void init() {
parseValidators();
}
public Validator getValidator(ValidatorConfig cfg) {
String className = lookupRegisteredValidatorType(cfg.getType());
Validator validator;
try {
// instantiate the validator, and set configured parameters
//todo - can this use the ThreadLocal?
validator = objectFactory.buildValidator(className, cfg.getParams(), ActionContext.getContext().getContextMap());
} catch (Exception e) {
final String msg = "There was a problem creating a Validator of type " + className + " : caused by " + e.getMessage();
throw new StrutsException(msg, e, cfg);
}
// set other configured properties
validator.setMessageKey(cfg.getMessageKey());
validator.setDefaultMessage(cfg.getDefaultMessage());
validator.setMessageParameters(cfg.getMessageParams());
if (validator instanceof ShortCircuitableValidator) {
((ShortCircuitableValidator) validator).setShortCircuit(cfg.isShortCircuit());
}
return validator;
}
public void registerValidator(String name, String className) {
LOG.debug("Registering validator of class {} with name {}", className, name);
validators.put(name, className);
}
public String lookupRegisteredValidatorType(String name) {
// lookup the validator class mapped to the type name
String className = validators.get(name);
if (className == null) {
throw new IllegalArgumentException("There is no validator class mapped to the name " + name);
}
return className;
}
private void parseValidators() {
LOG.debug("Loading validator definitions.");
List<File> files = new ArrayList<>();
try {
// Get custom validator configurations via the classpath
Iterator<URL> urls = ClassLoaderUtil.getResources("", DefaultValidatorFactory.class, false);
while (urls.hasNext()) {
URL u = urls.next();
try {
URI uri = new URI(u.toExternalForm().replaceAll(" ", "%20"));
if (!uri.isOpaque() && "file".equalsIgnoreCase(uri.getScheme())) {
File f = new File(uri);
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File file, String fileName) {
return fileName.contains("-validators.xml");
}
};
// First check if this is a directory
// If yes, then just do a "list" to get all files in this directory
// and match the filenames with *-validators.xml. If the filename
// matches then add to the list of files to be parsed
if (f.isDirectory()) {
try {
File[] ff = f.listFiles(filter);
if ( ff != null && ff.length > 0) {
files.addAll(Arrays.asList(ff));
}
} catch (SecurityException se) {
LOG.error("Security Exception while accessing directory '{}'", f, se);
}
} else {
// If this is not a directory, then get hold of the inputstream.
// If its not a ZipInputStream, then create a ZipInputStream out
// of it. The intention is to allow nested jar files to be scanned
// for *-validators.xml.
// Ex: struts-app.jar -> MyApp.jar -> Login-validators.xml should be
// parsed and loaded.
ZipInputStream zipInputStream = null;
try (InputStream inputStream = u.openStream()) {
if (inputStream instanceof ZipInputStream) {
zipInputStream = (ZipInputStream) inputStream;
} else {
zipInputStream = new ZipInputStream(inputStream);
}
ZipEntry zipEntry = zipInputStream.getNextEntry();
while (zipEntry != null) {
if (zipEntry.getName().endsWith("-validators.xml")) {
LOG.trace("Adding validator {}", zipEntry.getName());
files.add(new File(zipEntry.getName()));
}
zipEntry = zipInputStream.getNextEntry();
}
} finally {
//cleanup
if (zipInputStream != null) {
zipInputStream.close();
}
}
}
}
} catch (Exception ex) {
LOG.error("Unable to load {}", u, ex);
}
}
} catch (IOException e) {
throw new ConfigurationException("Unable to parse validators", e);
}
// Parse default validator configurations
String resourceName = "com/opensymphony/xwork2/validator/validators/default.xml";
retrieveValidatorConfiguration(resourceName);
// Overwrite and extend defaults with application specific validator configurations
resourceName = "validators.xml";
retrieveValidatorConfiguration(resourceName);
// Add custom (plugin) specific validator configurations
for (File file : files) {
retrieveValidatorConfiguration(file.getName());
}
}
private void retrieveValidatorConfiguration(String resourceName) {
InputStream is = ClassLoaderUtil.getResourceAsStream(resourceName, DefaultValidatorFactory.class);
if (is != null) {
validatorFileParser.parseValidatorDefinitions(validators, is, resourceName);
}
}
}