This is an automated email from the ASF dual-hosted git repository. gk pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/turbine-fulcrum-pool.git
commit ab6768f9302ff44d6a0584614a71a2199d00e34c Author: Siegfried Goeschl <[email protected]> AuthorDate: Thu Oct 25 16:29:42 2007 +0000 +) using fulcrum-yaafi-1.0.5 +) getting it to run with maven 1.1 git-svn-id: https://svn.apache.org/repos/asf/turbine/fulcrum/trunk/pool@588276 13f79535-47bb-0310-9956-ffa450edef68 --- .cvsignore | 6 + maven.xml | 26 + project.xml | 55 ++ .../apache/fulcrum/pool/ArrayCtorRecyclable.java | 40 ++ .../org/apache/fulcrum/pool/BoundedBuffer.java | 151 +++++ .../apache/fulcrum/pool/DefaultPoolService.java | 653 +++++++++++++++++++++ .../apache/fulcrum/pool/InitableRecyclable.java | 44 ++ .../fulcrum/pool/ObjectInputStreamForContext.java | 70 +++ .../org/apache/fulcrum/pool/PoolException.java | 47 ++ src/java/org/apache/fulcrum/pool/PoolService.java | 125 ++++ src/java/org/apache/fulcrum/pool/Recyclable.java | 57 ++ src/test/TestComponentConfig.xml | 27 + src/test/TestRoleConfig.xml | 32 + .../org/apache/fulcrum/pool/PoolServiceTest.java | 112 ++++ xdocs/changes.xml | 45 ++ xdocs/index.xml | 129 ++++ xdocs/navigation.xml | 37 ++ 17 files changed, 1656 insertions(+) diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..39db54d --- /dev/null +++ b/.cvsignore @@ -0,0 +1,6 @@ +target +*.log +.classpath +.project +jcoverage.ser +.merlin diff --git a/maven.xml b/maven.xml new file mode 100644 index 0000000..8c6e127 --- /dev/null +++ b/maven.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project default="jar:jar" xmlns:maven="jelly:maven" xmlns:j="jelly:core" xmlns:util="jelly:util"> + + <!--preGoal name="java:compile"> + <attainGoal name="avalon:meta"/> + </preGoal--> + +</project> diff --git a/project.xml b/project.xml new file mode 100644 index 0000000..bec02c5 --- /dev/null +++ b/project.xml @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<!-- + 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. +--> +<project> + <extend>${basedir}/../project.xml</extend> + <id>fulcrum-pool</id> + <name>Fulcrum Pool Component</name> + <currentVersion>1.0.3</currentVersion> + <versions> + <version> + <id>1.0.3</id> + <name>1.0.3</name> + <tag>FULCRUM_POOL_1_0_3</tag> + </version> + </versions> + <dependencies> + <dependency> + <groupId>fulcrum</groupId> + <artifactId>fulcrum-factory</artifactId> + <version>1.0.3</version> + </dependency> + + <!-- Needed only for testing --> + <dependency> + <groupId>fulcrum</groupId> + <artifactId>fulcrum-testcontainer</artifactId> + <version>1.0.5</version> + </dependency> + + <dependency> + <groupId>fulcrum</groupId> + <artifactId>fulcrum-yaafi</artifactId> + <version>1.0.5</version> + </dependency> + + </dependencies> + +</project> + diff --git a/src/java/org/apache/fulcrum/pool/ArrayCtorRecyclable.java b/src/java/org/apache/fulcrum/pool/ArrayCtorRecyclable.java new file mode 100644 index 0000000..fd335c0 --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/ArrayCtorRecyclable.java @@ -0,0 +1,40 @@ +package org.apache.fulcrum.pool; + + +/* + * 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. + */ + + +/** + * An interface for objects that can be pooled and + * recycled several times by different clients. This interface + * presents a recycle method that does not require introspection/reflection. + * + * @author <a href="mailto:[email protected]">John McNally</a> + * @version $Id$ + */ +public interface ArrayCtorRecyclable extends Recyclable +{ + /** + * Recycles the object for a new client. Objects implementing + * this interface must also provide a matching constructor. + * The recycle methods must call their super. + */ + public void recycle(Object[] params); +} diff --git a/src/java/org/apache/fulcrum/pool/BoundedBuffer.java b/src/java/org/apache/fulcrum/pool/BoundedBuffer.java new file mode 100644 index 0000000..40abd8d --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/BoundedBuffer.java @@ -0,0 +1,151 @@ +package org.apache.fulcrum.pool; + + +/* + * 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. + */ + + +/** + * Efficient array-based bounded buffer class. + * Adapted from CPJ, chapter 8, which describes design. + * Originally written by Doug Lea and released into the public domain. + * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] <p> + * + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @version $Id$ + */ +public class BoundedBuffer +{ + /** + * The default capacity. + */ + public static final int DEFAULT_CAPACITY = 1024; + + protected final Object[] array_; // the elements + + protected int takePtr_ = 0; // circular indices + protected int putPtr_ = 0; + + protected int usedSlots_ = 0; // length + protected int emptySlots_; // capacity - length + + /** + * Creates a buffer with the given capacity. + * + * @param capacity the capacity. + * @throws IllegalArgumentException if capacity less or equal to zero. + */ + public BoundedBuffer(int capacity) + throws IllegalArgumentException + { + if (capacity <= 0) + throw new IllegalArgumentException(); + + array_ = new Object[capacity]; + emptySlots_ = capacity; + } + + /** + * Creates a buffer with the default capacity + */ + public BoundedBuffer() + { + this(DEFAULT_CAPACITY); + } + + /** + * Returns the number of elements in the buffer. + * This is only a snapshot value, that may change + * immediately after returning. + * + * @return the size. + */ + public synchronized int size() + { + return usedSlots_; + } + + /** + * Returns the capacity of the buffer. + * + * @return the capacity. + */ + public int capacity() + { + return array_.length; + } + + /** + * Peeks, but does not remove the top item from the buffer. + * + * @return the object or null. + */ + public synchronized Object peek() + { + if (usedSlots_ > 0) + return array_[takePtr_]; + else + return null; + } + + /** + * Puts an item in the buffer only if there is capacity available. + * + * @param item the item to be inserted. + * @return true if accepted, else false. + */ + public synchronized boolean offer(Object x) + { + if (x == null) + throw new IllegalArgumentException(); + + if (emptySlots_ > 0) + { + --emptySlots_; + array_[putPtr_] = x; + if (++putPtr_ >= array_.length) + putPtr_ = 0; + usedSlots_++; + return true; + } + else + return false; + } + + /** + * Polls and removes the top item from the buffer if one is available. + * + * @return the oldest item from the buffer, or null if the buffer is empty. + */ + public synchronized Object poll() + { + if (usedSlots_ > 0) + { + --usedSlots_; + Object old = array_[takePtr_]; + array_[takePtr_] = null; + if (++takePtr_ >= array_.length) + takePtr_ = 0; + emptySlots_++; + return old; + } + else + return null; + } +} diff --git a/src/java/org/apache/fulcrum/pool/DefaultPoolService.java b/src/java/org/apache/fulcrum/pool/DefaultPoolService.java new file mode 100644 index 0000000..421ffda --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/DefaultPoolService.java @@ -0,0 +1,653 @@ +package org.apache.fulcrum.pool; + +/* + * 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.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.activity.Initializable; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.avalon.framework.configuration.Configurable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.fulcrum.factory.FactoryException; +import org.apache.fulcrum.factory.FactoryService; + +/** + * The Pool Service extends the Factory Service by adding support + * for pooling instantiated objects. When a new instance is + * requested, the service first checks its pool if one is available. + * If the the pool is empty, a new instance will be requested + * from the FactoryService. + * + * For objects implementing the Recyclable interface, a recycle + * method will be called, when they taken from the pool, and + * a dispose method, when they are returned to the pool. + * + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @author <a href="mailto:[email protected]">Stephen McConnell</a> + * @version $Id$ + * + * @avalon.component name="pool" lifestyle="transient" + * @avalon.service type="org.apache.fulcrum.pool.PoolService" + */ +public class DefaultPoolService extends AbstractLogEnabled implements PoolService, Serviceable, Disposable, Initializable, Configurable +{ + /** + * The property specifying the pool capacity. + */ + public static final String POOL_CAPACITY = "capacity"; + /** + * An inner class for class specific pools. + */ + private class PoolBuffer + { + /** + * An inner class for cached recycle methods. + */ + private class Recycler + { + /** + * The method. + */ + private final Method recycle; + /** + * The signature. + */ + private final String[] signature; + /** + * Constructs a new recycler. + * + * @param rec the recycle method. + * @param sign the signature. + */ + public Recycler(Method rec, String[] sign) + { + recycle = rec; + signature = (sign != null) && (sign.length > 0) ? sign : null; + } + /** + * Matches the given signature against + * that of the recycle method of this recycler. + * + * @param sign the signature. + * @return the matching recycle method or null. + */ + public Method match(String[] sign) + { + if ((sign != null) && (sign.length > 0)) + { + if ((signature != null) && (sign.length == signature.length)) + { + for (int i = 0; i < signature.length; i++) + { + if (!signature[i].equals(sign[i])) + { + return null; + } + } + return recycle; + } + else + { + return null; + } + } + else if (signature == null) + { + return recycle; + } + else + { + return null; + } + } + } + /** + * A buffer for class instances. + */ + private BoundedBuffer pool; + /** + * A flag to determine if a more efficient recycler is implemented. + */ + private boolean arrayCtorRecyclable; + /** + * A cache for recycling methods. + */ + private ArrayList recyclers; + /** + * Contructs a new pool buffer with a specific capacity. + * + * @param capacity a capacity. + */ + public PoolBuffer(int capacity) + { + pool = new BoundedBuffer(capacity); + } + /** + * Tells pool that it contains objects which can be + * initialized using an Object array. + * + * @param isArrayCtor a <code>boolean</code> value + */ + public void setArrayCtorRecyclable(boolean isArrayCtor) + { + arrayCtorRecyclable = isArrayCtor; + } + /** + * Polls for an instance from the pool. + * + * @return an instance or null. + */ + public Object poll(Object[] params, String[] signature) throws PoolException + { + Object instance = pool.poll(); + if (instance != null) + { + if (arrayCtorRecyclable) + { + ((ArrayCtorRecyclable) instance).recycle(params); + } + else if (instance instanceof Recyclable) + { + try + { + if ((signature != null) && (signature.length > 0)) + { + /* Get the recycle method from the cache. */ + Method recycle = getRecycle(signature); + if (recycle == null) + { + synchronized (this) + { + /* Make a synchronized recheck. */ + recycle = getRecycle(signature); + if (recycle == null) + { + Class clazz = instance.getClass(); + recycle = + clazz.getMethod( + "recycle", + getFactory().getSignature(clazz, params, signature)); + ArrayList cache = + recyclers != null ? (ArrayList) recyclers.clone() : new ArrayList(); + cache.add(new Recycler(recycle, signature)); + recyclers = cache; + } + } + } + recycle.invoke(instance, params); + } + else + { + ((Recyclable) instance).recycle(); + } + } + catch (Exception x) + { + throw new PoolException("Recycling failed for " + instance.getClass().getName(), x); + } + } + } + return instance; + } + /** + * Offers an instance to the pool. + * + * @param instance an instance. + */ + public boolean offer(Object instance) + { + if (instance instanceof Recyclable) + { + try + { + ((Recyclable) instance).dispose(); + } + catch (Exception x) + { + return false; + } + } + return pool.offer(instance); + } + /** + * Returns the capacity of the pool. + * + * @return the capacity. + */ + public int capacity() + { + return pool.capacity(); + } + /** + * Returns the size of the pool. + * + * @return the size. + */ + public int size() + { + return pool.size(); + } + /** + * Returns a cached recycle method + * corresponding to the given signature. + * + * @param signature the signature. + * @return the recycle method or null. + */ + private Method getRecycle(String[] signature) + { + ArrayList cache = recyclers; + if (cache != null) + { + Method recycle; + for (Iterator i = cache.iterator(); i.hasNext();) + { + recycle = ((Recycler) i.next()).match(signature); + if (recycle != null) + { + return recycle; + } + } + } + return null; + } + } + /** + * The default capacity of pools. + */ + private int poolCapacity = DEFAULT_POOL_CAPACITY; + /** + * The pool repository, one pool for each class. + */ + private HashMap poolRepository = new HashMap(); + private Map capacityMap; + private FactoryService factoryService; + private ServiceManager manager; + /** + * Constructs a Pool Service. + */ + public DefaultPoolService() + { + } + /** + * Gets an instance of a named class either from the pool + * or by calling the Factory Service if the pool is empty. + * + * @param className the name of the class. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(String className) throws PoolException + { + try + { + Object instance = pollInstance(className, null, null); + return instance == null ? getFactory().getInstance(className) : instance; + } + catch (FactoryException fe) + { + throw new PoolException(fe); + } + } + /** + * Gets an instance of a named class either from the pool + * or by calling the Factory Service if the pool is empty. + * The specified class loader will be passed to the Factory Service. + * + * @param className the name of the class. + * @param loader the class loader. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(String className, ClassLoader loader) throws PoolException + { + try + { + Object instance = pollInstance(className, null, null); + return instance == null ? getFactory().getInstance(className, loader) : instance; + } + catch (FactoryException fe) + { + throw new PoolException(fe); + } + } + /** + * Gets an instance of a named class either from the pool + * or by calling the Factory Service if the pool is empty. + * Parameters for its constructor are given as an array of objects, + * primitive types must be wrapped with a corresponding class. + * + * @param className the name of the class. + * @param loader the class loader. + * @param params an array containing the parameters of the constructor. + * @param signature an array containing the signature of the constructor. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(String className, Object[] params, String[] signature) throws PoolException + { + try + { + Object instance = pollInstance(className, params, signature); + return instance == null ? getFactory().getInstance(className, params, signature) : instance; + } + catch (FactoryException fe) + { + throw new PoolException(fe); + } + } + /** + * Gets an instance of a named class either from the pool + * or by calling the Factory Service if the pool is empty. + * Parameters for its constructor are given as an array of objects, + * primitive types must be wrapped with a corresponding class. + * The specified class loader will be passed to the Factory Service. + * + * @param className the name of the class. + * @param loader the class loader. + * @param params an array containing the parameters of the constructor. + * @param signature an array containing the signature of the constructor. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(String className, ClassLoader loader, Object[] params, String[] signature) + throws PoolException + { + try + { + Object instance = pollInstance(className, params, signature); + return instance == null ? getFactory().getInstance(className, loader, params, signature) : instance; + } + catch (FactoryException fe) + { + throw new PoolException(fe); + } + } + /** + * Tests if specified class loaders are supported for a named class. + * + * @param className the name of the class. + * @return true if class loaders are supported, false otherwise. + * @throws PoolException if test fails. + */ + public boolean isLoaderSupported(String className) throws FactoryException + { + return getFactory().isLoaderSupported(className); + } + /** + * Gets an instance of a specified class either from the pool + * or by instatiating from the class if the pool is empty. + * + * @param clazz the class. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(Class clazz) throws PoolException + { + try + { + Object instance = pollInstance(clazz.getName(), null, null); + return instance == null ? factoryService.getInstance(clazz) : instance; + } + catch (FactoryException fe) + { + throw new PoolException(fe); + } + } + /** + * Gets an instance of a specified class either from the pool + * or by instatiating from the class if the pool is empty. + * + * @todo There is a whacky .toString() on the clazzz, but otherwise it + * won't compile.. + * @param clazz the class. + * @param params an array containing the parameters of the constructor. + * @param signature an array containing the signature of the constructor. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(Class clazz, Object params[], String signature[]) throws PoolException + { + try + { + Object instance = pollInstance(clazz.getName(), params, signature); + //FactoryService fs = getFactory(); + return instance == null ? getFactory().getInstance(clazz.toString(), params, signature) : instance; + } + catch (FactoryException fe) + { + throw new PoolException(fe); + } + } + /** + * Puts a used object back to the pool. Objects implementing + * the Recyclable interface can provide a recycle method to + * be called when they are reused and a dispose method to be + * called when they are returned to the pool. + * + * @param instance the object instance to recycle. + * @return true if the instance was accepted. + */ + public boolean putInstance(Object instance) + { + if (instance != null) + { + HashMap repository = poolRepository; + String className = instance.getClass().getName(); + PoolBuffer pool = (PoolBuffer) repository.get(className); + if (pool == null) + { + pool = new PoolBuffer(getCapacity(className)); + repository = (HashMap) repository.clone(); + repository.put(className, pool); + poolRepository = repository; + if (instance instanceof ArrayCtorRecyclable) + { + pool.setArrayCtorRecyclable(true); + } + } + return pool.offer(instance); + } + else + { + return false; + } + } + /** + * Gets the capacity of the pool for a named class. + * + * @param className the name of the class. + */ + public int getCapacity(String className) + { + PoolBuffer pool = (PoolBuffer) poolRepository.get(className); + if (pool == null) + { + /* Check class specific capacity. */ + int capacity = poolCapacity; + if (capacityMap != null) + { + Integer cap = (Integer) capacityMap.get(className); + if (cap != null) + { + capacity = cap.intValue(); + } + } + return capacity; + } + else + { + return pool.capacity(); + } + } + /** + * Sets the capacity of the pool for a named class. + * Note that the pool will be cleared after the change. + * + * @param className the name of the class. + * @param capacity the new capacity. + */ + public void setCapacity(String className, int capacity) + { + HashMap repository = poolRepository; + repository = repository != null ? (HashMap) repository.clone() : new HashMap(); + repository.put(className, new PoolBuffer(capacity)); + poolRepository = repository; + } + /** + * Gets the current size of the pool for a named class. + * + * @param className the name of the class. + */ + public int getSize(String className) + { + PoolBuffer pool = (PoolBuffer) poolRepository.get(className); + return pool != null ? pool.size() : 0; + } + /** + * Clears instances of a named class from the pool. + * + * @param className the name of the class. + */ + public void clearPool(String className) + { + HashMap repository = poolRepository; + if (repository.get(className) != null) + { + repository = (HashMap) repository.clone(); + repository.remove(className); + poolRepository = repository; + } + } + /** + * Clears all instances from the pool. + */ + public void clearPool() + { + poolRepository = new HashMap(); + } + /** + * Polls and recycles an object of the named class from the pool. + * + * @param className the name of the class. + * @param params an array containing the parameters of the constructor. + * @param signature an array containing the signature of the constructor. + * @return the object or null. + * @throws PoolException if recycling fails. + */ + private Object pollInstance(String className, Object[] params, String[] signature) throws PoolException + { + PoolBuffer pool = (PoolBuffer) poolRepository.get(className); + return pool != null ? pool.poll(params, signature) : null; + } + /** + * Gets the factory service. + * + * @return the factory service. + */ + private FactoryService getFactory() + { + return factoryService; + } + // ---------------- Avalon Lifecycle Methods --------------------- + /** + * Avalon component lifecycle method + */ + public void configure(Configuration conf) + { + final Configuration capacities = conf.getChild(POOL_CAPACITY, false); + if (capacities != null) + { + Configuration defaultConf = capacities.getChild("default"); + int capacity = defaultConf.getValueAsInteger(DEFAULT_POOL_CAPACITY); + if (capacity <= 0) + { + throw new IllegalArgumentException("Capacity must be >0"); + } + poolCapacity = capacity; + Configuration[] nameVal = capacities.getChildren(); + for (int i = 0; i < nameVal.length; i++) + { + String key = nameVal[i].getName(); + if (!"default".equals(key)) + { + capacity = nameVal[i].getValueAsInteger(poolCapacity); + if (capacity < 0) + { + capacity = poolCapacity; + } + if (capacityMap == null) + { + capacityMap = new HashMap(); + } + capacityMap.put(key, new Integer(capacity)); + } + } + } + } + + /** + * Avalon component lifecycle method + * @avalon.dependency type="org.apache.fulcrum.factory.FactoryService" + */ + public void service(ServiceManager manager) + { + this.manager = manager; + } + + /** + * Avalon component lifecycle method + * Initializes the service by loading default class loaders + * and customized object factories. + * + * @throws InitializationException if initialization fails. + */ + public void initialize() throws Exception + { + try + { + factoryService = (FactoryService) manager.lookup(FactoryService.ROLE); + } + catch (Exception e) + { + throw new Exception( + "DefaultPoolService.initialize: Failed to get a Factory object", e); + } + } + + /** + * Avalon component lifecycle method + */ + public void dispose() + { + if (factoryService != null) + { + manager.release(factoryService); + } + factoryService = null; + manager = null; + } +} diff --git a/src/java/org/apache/fulcrum/pool/InitableRecyclable.java b/src/java/org/apache/fulcrum/pool/InitableRecyclable.java new file mode 100644 index 0000000..f6683a6 --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/InitableRecyclable.java @@ -0,0 +1,44 @@ +package org.apache.fulcrum.pool; + + +/* + * 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. + */ + + + + + +/** + * An interface for objects that can be pooled and recycled several times + * by different clients. Pooled objects that implement this interface + * use no argument ctor and recycle methods. Initialization is taken + * care of using the init method. This is a way to avoid + * introspection/reflection when pooling an object. + * + * @author <a href="mailto:[email protected]">John McNally</a> + * @version $Id$ + */ +public interface InitableRecyclable extends Recyclable +{ + /** + * This method should be called after retrieving the object from + * the pool. + */ + public void init(Object initObj) throws PoolException; +} diff --git a/src/java/org/apache/fulcrum/pool/ObjectInputStreamForContext.java b/src/java/org/apache/fulcrum/pool/ObjectInputStreamForContext.java new file mode 100644 index 0000000..9683ffe --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/ObjectInputStreamForContext.java @@ -0,0 +1,70 @@ +package org.apache.fulcrum.pool; + + +/* + * 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.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.io.IOException; + +/** + * A deserialization stream for a specific class loader context. + * + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @version $Id$ + */ +public class ObjectInputStreamForContext extends ObjectInputStream +{ + /** + * The class loader of the context. + */ + private ClassLoader classLoader; + + // this is to make the proxy happy. + public ObjectInputStreamForContext() + throws IOException + { + } + + /** + * Contructs a new object stream for a context. + * + * @param in the serialized input stream. + * @param loader the class loader of the context. + * @throws IOException on errors. + */ + public ObjectInputStreamForContext(InputStream in, + ClassLoader loader) + throws IOException + { + super(in); + classLoader = loader; + } + + protected Class resolveClass(ObjectStreamClass v) + throws IOException, + ClassNotFoundException + { + return classLoader == null ? + super.resolveClass(v) : classLoader.loadClass(v.getName()); + } +} diff --git a/src/java/org/apache/fulcrum/pool/PoolException.java b/src/java/org/apache/fulcrum/pool/PoolException.java new file mode 100644 index 0000000..7dd5d60 --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/PoolException.java @@ -0,0 +1,47 @@ +package org.apache.fulcrum.pool; + +/* + * 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. + */ + +/** + * Exception thrown when there is a problem with the PoolException + * + * @author <a href="mailto:[email protected]">Eric Pugh</a> + * @version $Id$ + */ +public class PoolException extends Exception +{ + /** + * Serial number + */ + private static final long serialVersionUID = 8192045560423973532L; + + public PoolException(String msg) + { + super(msg); + } + public PoolException(Exception ex) + { + super(ex); + } + public PoolException(String msg, Exception ex) + { + super(msg, ex); + } +} diff --git a/src/java/org/apache/fulcrum/pool/PoolService.java b/src/java/org/apache/fulcrum/pool/PoolService.java new file mode 100644 index 0000000..2c0d654 --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/PoolService.java @@ -0,0 +1,125 @@ +package org.apache.fulcrum.pool; + + +/* + * 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. + */ + + + +/** + * The Pool Service extends the Factory Service by adding support + * for pooling instantiated objects. When a new instance is + * requested, the service first checks its pool if one is available. + * If the the pool is empty, a new object will be instantiated + * from the specified class. If only class name is given, the request + * to create an instance will be forwarded to the Factory Service. + * + * <p>For objects implementing the Recyclable interface, a recycle + * method will be called, when they are taken from the pool, and + * a dispose method, when they are returned to the pool. + * + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @author <a href="mailto:[email protected]">Stephen McConnell</a> + * @version $Id$ + */ +public interface PoolService +{ + /** Avalon role - used to id the component within the manager */ + String ROLE = PoolService.class.getName(); + + + /** + * The default pool capacity. + */ + public static final int DEFAULT_POOL_CAPACITY = 128; + + /** + * Gets an instance of a specified class either from the pool + * or by instantiating from the class if the pool is empty. + * + * @param clazz the class. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(Class clazz) + throws PoolException; + + /** + * Gets an instance of a specified class either from the pool + * or by instantiating from the class if the pool is empty. + * + * @param clazz the class. + * @param params an array containing the parameters of the constructor. + * @param signature an array containing the signature of the constructor. + * @return the instance. + * @throws PoolException if recycling fails. + */ + public Object getInstance(Class clazz, + Object params[], + String signature[]) + throws PoolException; + + /** + * Puts a used object back to the pool. Objects implementing + * the Recyclable interface can provide a recycle method to + * be called when they are reused and a dispose method to be + * called when they are returned to the pool. + * + * @param instance the object instance to recycle. + * @return true if the instance was accepted. + */ + public boolean putInstance(Object instance); + + /** + * Gets the capacity of the pool for a named class. + * + * @param className the name of the class. + */ + public int getCapacity(String className); + + /** + * Sets the capacity of the pool for a named class. + * Note that the pool will be cleared after the change. + * + * @param className the name of the class. + * @param capacity the new capacity. + */ + public void setCapacity(String className, + int capacity); + + /** + * Gets the current size of the pool for a named class. + * + * @param className the name of the class. + */ + public int getSize(String className); + + /** + * Clears instances of a named class from the pool. + * + * @param className the name of the class. + */ + public void clearPool(String className); + + /** + * Clears all instances from the pool. + */ + void clearPool(); + +} diff --git a/src/java/org/apache/fulcrum/pool/Recyclable.java b/src/java/org/apache/fulcrum/pool/Recyclable.java new file mode 100644 index 0000000..762535a --- /dev/null +++ b/src/java/org/apache/fulcrum/pool/Recyclable.java @@ -0,0 +1,57 @@ +package org.apache.fulcrum.pool; + + +/* + * 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. + */ + + +/** + * An interface for objects that can be pooled and + * recycled several times by different clients. + * + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @version $Id$ + */ +public interface Recyclable +{ + /** + * Recycles the object for a new client. Recycle methods with + * parameters must be added to implementing object and they will be + * automatically called by pool implementations when the object is + * taken from the pool for a new client. The parameters must + * correspond to the parameters of the constructors of the object. + * For new objects, constructors can call their corresponding recycle + * methods whenever applicable. + * The recycle methods must call their super. + */ + public void recycle(); + + /** + * Disposes the object after use. The method is called + * when the object is returned to its pool. + * The dispose method must call its super. + */ + public void dispose(); + + /** + * Checks whether the recyclable has been disposed. + * @return true, if the recyclable is disposed. + */ + public boolean isDisposed(); +} diff --git a/src/test/TestComponentConfig.xml b/src/test/TestComponentConfig.xml new file mode 100644 index 0000000..dcb4cdd --- /dev/null +++ b/src/test/TestComponentConfig.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<componentConfig> + <pool> + <configuration> + <capacity default="256"/> + </configuration> + </pool> + <factory/> +</componentConfig> diff --git a/src/test/TestRoleConfig.xml b/src/test/TestRoleConfig.xml new file mode 100644 index 0000000..20b744e --- /dev/null +++ b/src/test/TestRoleConfig.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<!-- This configuration file for Avalon components is used for testing the TestComponent --> +<role-list> + <role + name="org.apache.fulcrum.factory.FactoryService" + shorthand="factory" + default-class="org.apache.fulcrum.factory.DefaultFactoryService" + /> + <role + name="org.apache.fulcrum.pool.PoolService" + shorthand="pool" + default-class="org.apache.fulcrum.pool.DefaultPoolService" + /> +</role-list> diff --git a/src/test/org/apache/fulcrum/pool/PoolServiceTest.java b/src/test/org/apache/fulcrum/pool/PoolServiceTest.java new file mode 100644 index 0000000..b8c5d73 --- /dev/null +++ b/src/test/org/apache/fulcrum/pool/PoolServiceTest.java @@ -0,0 +1,112 @@ +package org.apache.fulcrum.pool; + +/* + * 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 org.apache.fulcrum.testcontainer.BaseUnitTest; + +/** + * @author Eric Pugh + * @author <a href="mailto:[email protected]">Stephen McConnell</a> + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class PoolServiceTest extends BaseUnitTest +{ + private PoolService poolService = null; + /** + * Defines the testcase name for JUnit. + * + * @param name the testcase's name. + */ + public PoolServiceTest(String name) + { + super(name); + } + + public void setUp() throws Exception + { + super.setUp(); + + poolService = (PoolService) this.resolve( PoolService.class.getName() ); + } + + /* + * Class to test for Object getInstance(Class) + */ + public void testGetInstanceClass() throws PoolException + { + Object object = poolService.getInstance(StringBuffer.class); + assertTrue(object instanceof StringBuffer); + + } + + public void testPutInstance() + { + String s = "I am a string"; + assertEquals(0, poolService.getSize("java.lang.String")); + poolService.putInstance(s); + assertEquals(1, poolService.getSize("java.lang.String")); + + } + public void testGetSetCapacity() + { + assertEquals(128, poolService.getCapacity("java.lang.String")); + poolService.setCapacity("java.lang.String", 278); + assertEquals(278, poolService.getCapacity("java.lang.String")); + + } + public void testGetSize() + { + String s = "I am a string"; + assertEquals(0, poolService.getSize("java.lang.String")); + poolService.putInstance(s); + assertEquals(1, poolService.getSize("java.lang.String")); + + } + /* + * Class to test for void clearPool(String) + */ + public void testClearPoolString() + { + String s = "I am a string"; + assertEquals(0, poolService.getSize("java.lang.String")); + poolService.putInstance(s); + assertEquals(1, poolService.getSize("java.lang.String")); + poolService.clearPool("java.lang.String"); + assertEquals(0, poolService.getSize("java.lang.String")); + + } + /* + * Class to test for void clearPool() + */ + public void testClearPool() + { + String s = "I am a string"; + assertEquals(0, poolService.getSize("java.lang.String")); + poolService.putInstance(s); + poolService.putInstance(new Double(32)); + assertEquals(1, poolService.getSize("java.lang.String")); + poolService.clearPool(); + assertEquals(0, poolService.getSize("java.lang.String")); + assertEquals(0, poolService.getSize("java.lang.Double")); + + } +} diff --git a/xdocs/changes.xml b/xdocs/changes.xml new file mode 100644 index 0000000..cfae165 --- /dev/null +++ b/xdocs/changes.xml @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<!-- + 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. +--> +<document> + <properties> + <title>Fulcrum Pool Impl</title> + <author email="[email protected]">Eric Pugh</author> + </properties> + + <body> + <release version="1.0.3" date="2004-11-24"> + <action dev="epugh" type="update" due-to="Kostyantyn Shchekotykhin"> + Merge api and impl jars into one project. + </action> + <action dev="epugh" type="update"> + Clean up dependencies. Use TestContainer. + </action> + <action dev="epugh" type="update"> + Bump versions, clean up dependencies + </action> + </release> + <release version="1.0.2" date="in cvs"> + <action dev="epugh" type="update"> + Update to use Merlin 3.3.0 + </action> + </release> + </body> +</document> + diff --git a/xdocs/index.xml b/xdocs/index.xml new file mode 100644 index 0000000..f50c7f1 --- /dev/null +++ b/xdocs/index.xml @@ -0,0 +1,129 @@ +<?xml version="1.0"?> +<!-- + 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. +--> + +<document> + + <properties> + <title>Pool Component</title> + <author email="[email protected]">Eric PUgh</author> + </properties> + + <body> + + <section name="Overview"> + +<p> +The Pool Service extends the functionality of the Factory Service by adding +support for pooling objects intantiated from the given class name or +Class object reference. Pooling of objects stabilizes memory consumption and +reduces garbage collection making response times in server applications +more predictable. +</p> + +<p> +When a new instance is requested from the service, it first checks its pool +if one is available. If the the pool is empty, a new object will be instantiated +from the given class. If the class is specified by its name, the request to create +an instance will be forwarded to the Factory Service. +</p> + +<p> +For pooled objects implementing the Recyclable interface, a recycle method +will be called, when they are taken from the pool, and a dispose method, +when they are returned to the pool. Implementations of the methods should +clear and initialize the pooled instances correspondingly. Objects +that do not implement the interface can also be pooled, if they do not +need to perform any specific actions during pooling. A RecyclableSupport class +can be extended to get a minimal implementation of the interface. +</p> + +<p> +An ArrayCtorRecyclable interface extends the Recyclable interface providing +a more efficient recycle method with less reflection for recycling frequently +used objects having constuctors with parameters. +</p> + </section> + +<section name="Configuration"> + + <p> + First, here is the role configuration. This component requires the FactoryService + component. + </p> + +<source> +<![CDATA[ + <role + name="org.apache.fulcrum.pool.PoolService" + shorthand="pool" + default-class="org.apache.fulcrum.pool.DefaultPoolService"/> + + + <role + name="org.apache.fulcrum.factory.FactoryService" + shorthand="factory" + default-class="org.apache.fulcrum.factory.DefaultFactoryService"/> +]]> +</source> + + <p> + Now comes the basic configuration of the component. Here will will + configure the various bundles. + </p> +<source> + +<![CDATA[ + <pool> + <capacity default="256"/> + </pool> + + <factory/> +]]> +</source> + + </section> + + <section name="Usage"> + +<p> +The Pool Service can be called instead of the Factory Service, when instantiating +objects that are needed repeatedly e.g. for processing client requests. Intances +of RunData implementations, ParameterParser and CookieParser implementations, +Pull Service tools, etc, are typical examples of pooled objects. Used objects +must be returned to the Pool Service for recycling. The TurbinePool class is a +static accessor for common methods of the Pool Service: +</p> + +<source><![CDATA[ +// Get a pooled DOM parser. +DocumentBuilder parser = + TurbinePool.getInstance("javax.xml.parsers.DocumentBuilder"); + +// Parse an XML document. +Document doc = parser.parse(myfile); + +// Return the parser to the pool. +TurbinePool.putInstance(parser); +]]></source> + + </section> + +</body> +</document> diff --git a/xdocs/navigation.xml b/xdocs/navigation.xml new file mode 100644 index 0000000..6e7be15 --- /dev/null +++ b/xdocs/navigation.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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. +--> + +<!DOCTYPE project [ +<!ENTITY site-nav SYSTEM "../../incl_site_nav.xml"> +]> + +<project + name="Fulcrum Pool" + href="http://turbine.apache.org/fulcrum/fulcrum-pool/"> + + <body> + +&site-nav; + + <menu name="Overview"> + <item name="Main" href="/index.html"/> + </menu> + </body> +</project>
