/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wps.validator; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.geoserver.config.GeoServer; import org.geoserver.platform.ExtensionPriority; import org.geoserver.wps.ProcessGroupInfo; import org.geoserver.wps.ProcessInfo; import org.geoserver.wps.WPSInfo; import org.geoserver.wps.ppio.ProcessParameterIO; import org.geoserver.wps.process.DelegatingProcessFactory; import org.geoserver.wps.process.GeoServerProcessors; import org.geoserver.wps.process.ProcessFilter; import org.geotools.data.Parameter; import org.geotools.process.ProcessFactory; import org.geotools.util.Range; import org.geotools.util.logging.Logging; import org.opengis.feature.type.Name; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.validation.Validator; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; /** * A process filter that applies the well known input validators to the process parameters, so that * DescribeFeatureType can advertise them to the world * * @author Andrea Aime - GeoSolutions * */ public class ProcessLimitsFilter implements ProcessFilter, ApplicationContextAware, ExtensionPriority { static final Logger LOGGER = Logging.getLogger(ProcessLimitsFilter.class); static final Multimap<String, WPSInputValidator> EMPTY_MULTIMAP = ImmutableMultimap.of(); /** * Key where the parameter validators will be stored */ public static final String VALIDATORS_KEY = "wpsValidators"; GeoServer geoServer; private ApplicationContext applicationContext; public ProcessLimitsFilter(GeoServer geoServer) { this.geoServer = geoServer; } @Override public ProcessFactory filterFactory(ProcessFactory pf) { return new ProcessLimitFactory(pf); } class ProcessLimitFactory extends DelegatingProcessFactory { public ProcessLimitFactory(ProcessFactory delegate) { super(delegate); } @Override public Map<String, Parameter<?>> getParameterInfo(Name name) { // look for the ProcessInfo for this process, it might have restrictions WPSInfo wps = geoServer.getService(WPSInfo.class); ProcessFactory factory = GeoServerProcessors.createProcessFactory(name, false); ProcessInfo processInfo = null; for (ProcessGroupInfo group : wps.getProcessGroups()) { if (group.getFactoryClass().equals(factory.getClass())) { List<ProcessInfo> filteredProcesses = group.getFilteredProcesses(); for (ProcessInfo pi : filteredProcesses) { if (name.equals(pi.getName())) { processInfo = pi; break; } } if (processInfo != null) { break; } } } // get the parameters, see if we have any restriction to apply Map<String, Parameter<?>> result = super.getParameterInfo(name); int maxComplexInputSize = wps.getMaxComplexInputSize(); if (maxComplexInputSize <= 0 && (processInfo == null || processInfo.getValidators() == null || processInfo .getValidators().isEmpty())) { return result; } else { Multimap<String, WPSInputValidator> validatorsMap = processInfo != null ? processInfo .getValidators() : EMPTY_MULTIMAP; // clone just to be on the safe side HashMap<String, Parameter<?>> params = new LinkedHashMap<>(result); for (String paramName : params.keySet()) { Parameter<?> param = params.get(paramName); Collection<WPSInputValidator> validators = validatorsMap.get(paramName); // can we skip to build a clone? if (validators == null && (maxComplexInputSize <= 0 || !ProcessParameterIO.isComplex(param, applicationContext))) { continue; } // setup the global size limits. non complex params will just ignore it Map<String, Object> metadataClone = new HashMap(param.metadata); if (wps.getMaxComplexInputSize() > 0) { metadataClone.put(MaxSizeValidator.PARAMETER_KEY, wps.getMaxComplexInputSize()); } // collect all validator overrides int maxOccurs = param.getMaxOccurs(); if(validators != null) { metadataClone.put(VALIDATORS_KEY, validators); for (Validator validator : validators) { if (validator instanceof MaxSizeValidator) { MaxSizeValidator msv = (MaxSizeValidator) validator; int maxSizeMB = msv.getMaxSizeMB(); metadataClone.put(MaxSizeValidator.PARAMETER_KEY, maxSizeMB); } else if (validator instanceof NumberRangeValidator) { NumberRangeValidator rv = (NumberRangeValidator) validator; Range<?> range = rv.getRange(); Comparable min = (Comparable) param.metadata.get(Parameter.MIN); Comparable max = (Comparable) param.metadata.get(Parameter.MAX); boolean restricting = false; if (range.getMinValue() != null && (min == null || min.compareTo(range.getMinValue()) < 0)) { min = range.getMinValue(); restricting = true; } if (range.getMaxValue() != null && (max == null || max.compareTo(range.getMaxValue()) > 0)) { max = range.getMaxValue(); restricting = true; } if (restricting) { if (min != null) { metadataClone.put(Parameter.MIN, min); } if (max != null) { metadataClone.put(Parameter.MAX, max); } } } else if (validator instanceof MultiplicityValidator) { MultiplicityValidator mv = (MultiplicityValidator) validator; int max = mv.getMaxInstances(); if (max < maxOccurs) { maxOccurs = max; } } } } // rebuild the param and put it in the params Parameter<?> substitute = new Parameter(param.key, param.type, param.title, param.description, param.required, param.minOccurs, maxOccurs, param.sample, metadataClone); params.put(param.key, substitute); } return params; } } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public int getPriority() { return Integer.MAX_VALUE; } }