| package org.apache.fulcrum.parser; |
| |
| /* |
| * 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. |
| */ |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.Part; |
| |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.logger.AbstractLogEnabled; |
| import org.apache.avalon.framework.logger.LogEnabled; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.pool2.impl.GenericObjectPoolConfig; |
| import org.apache.fulcrum.parser.ValueParser.URLCaseFolding; |
| import org.apache.fulcrum.parser.pool.BaseValueParserFactory; |
| import org.apache.fulcrum.parser.pool.BaseValueParserPool; |
| import org.apache.fulcrum.parser.pool.CookieParserFactory; |
| import org.apache.fulcrum.parser.pool.CookieParserPool; |
| import org.apache.fulcrum.parser.pool.DefaultParameterParserFactory; |
| import org.apache.fulcrum.parser.pool.DefaultParameterParserPool; |
| import org.apache.fulcrum.pool.PoolException; |
| import org.apache.fulcrum.pool.PoolService; |
| |
| |
| /** |
| * The DefaultParserService provides the default implementation |
| * of a {@link ParserService}. |
| * |
| * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a> |
| * @version $Id: BaseValueParser.java 542062 2007-05-28 00:29:43Z seade $ |
| */ |
| public class DefaultParserService |
| extends AbstractLogEnabled |
| implements ParserService, |
| Configurable, Serviceable |
| { |
| |
| /** The folding from the configuration */ |
| private URLCaseFolding folding = URLCaseFolding.NONE; |
| |
| /** The automaticUpload setting from the configuration */ |
| private boolean automaticUpload = AUTOMATIC_DEFAULT; |
| |
| /** |
| * The parameter encoding to use when parsing parameter strings |
| */ |
| private String parameterEncoding = PARAMETER_ENCODING_DEFAULT; |
| |
| /** |
| * reintroduced fulcrum, may be used in the case, that |
| * commons pool2 is not configured exactly as needed properly as fast fall back. |
| */ |
| private boolean useFulcrumPool = FULCRUM_POOL_DEFAULT; |
| |
| |
| /** |
| * The Fulcrum pool service component to use (optional), by default it is deactivated and commons pool is used. |
| */ |
| private PoolService fulcrumPoolService = null; |
| |
| /** |
| * Use commons pool to manage value parsers |
| */ |
| private BaseValueParserPool valueParserPool; |
| |
| /** |
| * Use commons pool to manage parameter parsers |
| */ |
| private DefaultParameterParserPool parameterParserPool; |
| |
| /** |
| * Use commons pool to manage cookie parsers |
| */ |
| private CookieParserPool cookieParserPool; |
| |
| |
| public DefaultParserService() |
| { |
| } |
| |
| public DefaultParserService(GenericObjectPoolConfig<?> config) |
| { |
| // init the pool |
| valueParserPool |
| = new BaseValueParserPool(new BaseValueParserFactory(), config); |
| |
| parameterParserPool |
| = new DefaultParameterParserPool(new DefaultParameterParserFactory(), config); |
| } |
| |
| |
| /** |
| * Get the character encoding that will be used by this ValueParser. |
| */ |
| @Override |
| public String getParameterEncoding() |
| { |
| return parameterEncoding; |
| } |
| |
| /** |
| * Set the character encoding that will be used by this ValueParser. |
| */ |
| public void setParameterEncoding(String encoding) |
| { |
| parameterEncoding = encoding; |
| } |
| |
| /** |
| * Trims the string data and applies the conversion specified in |
| * the property given by URL_CASE_FOLDING. It returns a new |
| * string so that it does not destroy the value data. |
| * |
| * @param value A String to be processed. |
| * @return A new String converted to the case as specified by URL_CASE_FOLDING and trimmed. |
| */ |
| @Override |
| public String convert(String value) |
| { |
| return convertAndTrim(value); |
| } |
| |
| /** |
| * Convert a String value according to the url-case-folding property. |
| * |
| * @param value the String to convert |
| * |
| * @return a new String. |
| * |
| */ |
| @Override |
| public String convertAndTrim(String value) |
| { |
| return convertAndTrim(value, getUrlFolding()); |
| } |
| |
| /** |
| * A static version of the convert method, which |
| * trims the string data and applies the conversion specified in |
| * the property given by URL_CASE_FOLDING. It returns a new |
| * string so that it does not destroy the value data. |
| * |
| * @param value A String to be processed. |
| * @return A new String converted to lowercase and trimmed. |
| */ |
| @Override |
| public String convertAndTrim(String value, URLCaseFolding fold) |
| { |
| if (value == null) |
| { |
| return ""; |
| } |
| |
| String tmp = value.trim(); |
| |
| switch (fold) |
| { |
| case NONE: |
| { |
| break; |
| } |
| |
| case LOWER: |
| { |
| tmp = tmp.toLowerCase(); |
| break; |
| } |
| |
| case UPPER: |
| { |
| tmp = tmp.toUpperCase(); |
| break; |
| } |
| |
| default: |
| { |
| getLogger().error("Passed " + fold + " as fold rule, which is illegal!"); |
| break; |
| } |
| } |
| return tmp; |
| } |
| |
| /** |
| * Gets the folding value from the configuration |
| * |
| * @return The current Folding Value |
| */ |
| @Override |
| public URLCaseFolding getUrlFolding() |
| { |
| return folding; |
| } |
| |
| /** |
| * Gets the automaticUpload value from the configuration |
| * |
| * @return The current automaticUpload Value |
| */ |
| @Override |
| public boolean getAutomaticUpload() |
| { |
| return automaticUpload; |
| } |
| |
| /** |
| * Parse the given request for uploaded files |
| * |
| * @return A list of {@link javax.servlet.http.Part}s |
| * |
| * @throws ServiceException if parsing fails |
| */ |
| @Override |
| public List<Part> parseUpload(HttpServletRequest request) throws ServiceException |
| { |
| try |
| { |
| return new ArrayList<Part>(request.getParts()); |
| } |
| catch (IOException | ServletException e) |
| { |
| throw new ServiceException(ParserService.ROLE, "Could not parse upload request", e); |
| } |
| } |
| |
| /** |
| * Get a {@link ValueParser} instance from the service. Use the |
| * given Class to create the object. |
| * |
| * @return An object that implements ValueParser |
| * |
| * @throws InstantiationException if the instance could not be created |
| */ |
| @SuppressWarnings("unchecked") |
| @Override |
| public <P extends ValueParser> P getParser(Class<P> ppClass) throws InstantiationException |
| { |
| P vp = null; |
| |
| try |
| { |
| if (useFulcrumPool) { |
| try |
| { |
| P parserInstance = (P) fulcrumPoolService.getInstance(ppClass); |
| vp = parserInstance; |
| } |
| catch (PoolException pe) |
| { |
| throw new InstantiationException("Parser class '" + ppClass + "' is illegal. " + pe.getMessage()); |
| } |
| } else if ( ppClass.equals(BaseValueParser.class) ) |
| { |
| BaseValueParser parserInstance = null; |
| try { |
| parserInstance = valueParserPool.borrowObject(); |
| vp = (P) parserInstance; |
| if (vp == null) { |
| throw new InstantiationException("Could not borrow object from pool: " + valueParserPool); |
| } |
| } catch (Exception e) { |
| try { |
| valueParserPool.invalidateObject(parserInstance); |
| parserInstance = null; |
| } catch (Exception e1) { |
| throw new InstantiationException("Could not invalidate object " + e1.getMessage() + " after exception: " + e.getMessage()); |
| } |
| } |
| } else if ( ppClass.equals(DefaultParameterParser.class) ) |
| { |
| DefaultParameterParser parserInstance = null; |
| try { |
| parserInstance = parameterParserPool.borrowObject(); |
| vp = (P) parserInstance; |
| if (vp == null) { |
| throw new InstantiationException("Could not borrow object from pool: " + parameterParserPool); |
| } |
| } catch (Exception e) { |
| try { |
| parameterParserPool.invalidateObject(parserInstance); |
| parserInstance = null; |
| } catch (Exception e1) { |
| throw new InstantiationException("Could not invalidate object " + e1.getMessage() + " after exception: " + e.getMessage()); |
| } |
| } |
| } else if ( ppClass.equals(DefaultCookieParser.class) ) |
| { |
| DefaultCookieParser parserInstance = null; |
| try { |
| parserInstance = cookieParserPool.borrowObject(); |
| vp = (P) parserInstance; |
| if (vp == null) { |
| throw new InstantiationException("Could not borrow object from pool: " + cookieParserPool); |
| } |
| } catch (Exception e) { |
| try { |
| cookieParserPool.invalidateObject(parserInstance); |
| parserInstance = null; |
| } catch (Exception e1) { |
| throw new InstantiationException("Could not invalidate object " + e1.getMessage() + " after exception: " + e.getMessage()); |
| } |
| } |
| } |
| |
| if (vp != null && vp instanceof ParserServiceSupport ) { |
| ((ParserServiceSupport)vp).setParserService(this); |
| } else { |
| throw new InstantiationException("Could not set parser"); |
| } |
| if (vp instanceof LogEnabled) |
| { |
| ((LogEnabled)vp).enableLogging(getLogger().getChildLogger(ppClass.getSimpleName())); |
| } |
| } |
| catch (ClassCastException x) |
| { |
| throw new InstantiationException("Parser class '" + ppClass + "' is illegal. " + x.getMessage()); |
| } |
| |
| return vp; |
| } |
| |
| /** |
| * Clears the parse and puts it back into |
| * the pool service. This allows for pooling |
| * and recycling |
| * |
| * As we are not yet using org.apache.fulcrum.pool.Recyclable, we call insteda {@link ValueParser#dispose()}. |
| * |
| * @param parser The value parser to use |
| */ |
| @Override |
| public void putParser(ValueParser parser) |
| { |
| parser.clear(); |
| parser.dispose(); |
| |
| if (useFulcrumPool) { |
| |
| fulcrumPoolService.putInstance(parser); |
| |
| } else if( parser.getClass().equals(BaseValueParser.class) ) |
| { |
| valueParserPool.returnObject( (BaseValueParser) parser ); |
| |
| } else if ( parser.getClass().equals(DefaultParameterParser.class) || |
| parser instanceof DefaultParameterParser) |
| { |
| parameterParserPool.returnObject( (DefaultParameterParser) parser ); |
| |
| } else if ( parser.getClass().equals(DefaultCookieParser.class) || |
| parser instanceof DefaultCookieParser) |
| { |
| cookieParserPool.returnObject( (DefaultCookieParser) parser ); |
| |
| } else { |
| // log |
| getLogger().warn(parser.getClass() + " could not be put back into any pool exhausting some pool"); |
| // log even borrowed count of each pool?: cookieParserPool.getBorrowedCount()) |
| } |
| } |
| |
| /** |
| * Avalon component lifecycle method |
| * |
| * @param conf the configuration |
| * @throws ConfigurationException Generic exception |
| */ |
| @Override |
| public void configure(Configuration conf) throws ConfigurationException |
| { |
| String foldString = conf.getChild(URL_CASE_FOLDING_KEY).getValue(URLCaseFolding.NONE.name()).toLowerCase(); |
| |
| folding = URLCaseFolding.NONE; |
| |
| getLogger().debug("Setting folding from " + foldString); |
| |
| if (StringUtils.isNotEmpty(foldString)) |
| { |
| try |
| { |
| folding = URLCaseFolding.valueOf(foldString.toUpperCase()); |
| } |
| catch (IllegalArgumentException e) |
| { |
| getLogger().error("Got " + foldString + " from " + URL_CASE_FOLDING_KEY + " property, which is illegal!"); |
| throw new ConfigurationException("Value " + foldString + " is illegal!", e); |
| } |
| } |
| |
| parameterEncoding = conf.getChild(PARAMETER_ENCODING_KEY) |
| .getValue(PARAMETER_ENCODING_DEFAULT).toLowerCase(); |
| |
| automaticUpload = conf.getChild(AUTOMATIC_KEY).getValueAsBoolean(AUTOMATIC_DEFAULT); |
| |
| useFulcrumPool = conf.getChild(FULCRUM_POOL_KEY).getValueAsBoolean(FULCRUM_POOL_DEFAULT); |
| |
| if (useFulcrumPool) { |
| if (fulcrumPoolService == null) |
| { |
| // only for fulcrum pool, need to call internal service, if role pool service is set |
| throw new ConfigurationException("Fulcrum Pool is activated", new ServiceException(ParserService.ROLE, |
| "Fulcrum enabled Pool Service requires " + |
| PoolService.ROLE + " to be available")); |
| } |
| getLogger().info("Using Fulcrum Pool Service: "+ fulcrumPoolService); |
| } else { |
| // reset not used fulcrum pool |
| fulcrumPoolService = null; |
| |
| // Define the default configuration |
| GenericObjectPoolConfig config = new GenericObjectPoolConfig(); |
| config.setMaxIdle(DEFAULT_MAX_IDLE); |
| config.setMaxTotal(DEFAULT_POOL_CAPACITY); |
| |
| // init the pool |
| valueParserPool |
| = new BaseValueParserPool(new BaseValueParserFactory(), config); |
| |
| // init the pool |
| parameterParserPool |
| = new DefaultParameterParserPool(new DefaultParameterParserFactory(), config); |
| |
| // init the pool |
| cookieParserPool |
| = new CookieParserPool(new CookieParserFactory(), config); |
| |
| getLogger().info("Init Commons2 Pool Services.." ); |
| getLogger().info(valueParserPool.getClass().getName()); |
| getLogger().info(parameterParserPool.getClass().getName()); |
| getLogger().info(cookieParserPool.getClass().getName()); |
| } |
| |
| Configuration[] poolChildren = conf.getChild(POOL_KEY).getChildren(); |
| if (poolChildren.length > 0) { |
| GenericObjectPoolConfig genObjPoolConfig = new GenericObjectPoolConfig(); |
| genObjPoolConfig.setMaxIdle(DEFAULT_MAX_IDLE); |
| genObjPoolConfig.setMaxTotal(DEFAULT_POOL_CAPACITY); |
| for (Configuration poolConf : poolChildren) { |
| // use common pool2 configuration names |
| switch (poolConf.getName()) { |
| case "maxTotal": |
| int defaultCapacity = poolConf.getValueAsInteger(); |
| genObjPoolConfig.setMaxTotal(defaultCapacity); |
| break; |
| case "maxWaitMillis": |
| int maxWaitMillis = poolConf.getValueAsInteger(); |
| genObjPoolConfig.setMaxWaitMillis(maxWaitMillis); |
| break; |
| case "blockWhenExhausted": |
| boolean blockWhenExhausted = poolConf.getValueAsBoolean(); |
| genObjPoolConfig.setBlockWhenExhausted(blockWhenExhausted); |
| break; |
| case "maxIdle": |
| int maxIdle = poolConf.getValueAsInteger(); |
| genObjPoolConfig.setMaxIdle(maxIdle); |
| break; |
| case "minIdle": |
| int minIdle = poolConf.getValueAsInteger(); |
| genObjPoolConfig.setMinIdle(minIdle); |
| break; |
| case "testOnReturn": |
| boolean testOnReturn = poolConf.getValueAsBoolean(); |
| genObjPoolConfig.setTestOnReturn(testOnReturn); |
| break; |
| case "testOnBorrow": |
| boolean testOnBorrow = poolConf.getValueAsBoolean(); |
| genObjPoolConfig.setTestOnBorrow(testOnBorrow); |
| break; |
| case "testOnCreate": |
| boolean testOnCreate = poolConf.getValueAsBoolean(); |
| genObjPoolConfig.setTestOnCreate(testOnCreate); |
| break; |
| default: |
| |
| break; |
| } |
| } |
| // reinit the pools |
| valueParserPool.setConfig(genObjPoolConfig); |
| parameterParserPool.setConfig(genObjPoolConfig); |
| cookieParserPool.setConfig(genObjPoolConfig); |
| |
| getLogger().debug(valueParserPool.toString()); |
| getLogger().debug(parameterParserPool.toString()); |
| getLogger().debug(cookieParserPool.toString()); |
| } |
| |
| } |
| |
| // ---------------- Avalon Lifecycle Methods --------------------- |
| /** |
| * Avalon component lifecycle method |
| * |
| * @param manager The service manager instance |
| * @throws ServiceException generic exception |
| * |
| */ |
| @Override |
| public void service(ServiceManager manager) throws ServiceException |
| { |
| // only for fulcrum pool, need to call internal service, if role pool service is set |
| if (manager.hasService(PoolService.ROLE)) |
| { |
| fulcrumPoolService = (PoolService)manager.lookup(PoolService.ROLE); |
| } |
| // no check here, until configuration is read |
| } |
| } |