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-factory.git
commit 4a3b1ba1d93638036c69d1bec06474c38f0fd755 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/factory@588276 13f79535-47bb-0310-9956-ffa450edef68 --- .cvsignore | 6 + maven.xml | 26 + project.xml | 50 ++ .../fulcrum/factory/DefaultFactoryService.java | 544 +++++++++++++++++++++ src/java/org/apache/fulcrum/factory/Factory.java | 110 +++++ .../apache/fulcrum/factory/FactoryException.java | 43 ++ .../org/apache/fulcrum/factory/FactoryService.java | 140 ++++++ .../factory/utils/ObjectInputStreamForContext.java | 70 +++ src/test/TestComponentConfig.xml | 23 + src/test/TestRoleConfig.xml | 28 ++ .../apache/fulcrum/factory/FactoryServiceTest.java | 131 +++++ .../utils/ObjectInputStreamForContextTest.java | 57 +++ xdocs/changes.xml | 42 ++ xdocs/index.xml | 198 ++++++++ xdocs/navigation.xml | 38 ++ xdocs/tasks.xml | 49 ++ 16 files changed, 1555 insertions(+) diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..e97dd78 --- /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..47be3fe --- /dev/null +++ b/project.xml @@ -0,0 +1,50 @@ +<?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-factory</id> + <name>Fulcrum Factory Component</name> + <currentVersion>1.0.3</currentVersion> + <versions> + <version> + <id>1.0.3</id> + <name>1.0.3</name> + <tag>FULCRUM_FACTORY_1_0_3</tag> + </version> + </versions> + <dependencies> + + <!-- 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/factory/DefaultFactoryService.java b/src/java/org/apache/fulcrum/factory/DefaultFactoryService.java new file mode 100644 index 0000000..86472ca --- /dev/null +++ b/src/java/org/apache/fulcrum/factory/DefaultFactoryService.java @@ -0,0 +1,544 @@ +package org.apache.fulcrum.factory; + +/* + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import org.apache.avalon.framework.activity.Initializable; +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.fulcrum.factory.utils.ObjectInputStreamForContext; + +/** + * The Factory Service instantiates objects using specified + * class loaders. If none is specified, the default one + * will be used. + * + * @author <a href="mailto:[email protected]">Eric Pugh</a> + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @author <a href="mailto:[email protected]">Stephen McConnell</a> + * @version $Id$ + * + * @avalon.component name="factory" lifestyle="singleton" + * @avalon.service type="org.apache.fulcrum.factory.FactoryService" + */ +public class DefaultFactoryService + extends AbstractLogEnabled + implements FactoryService, Configurable, Initializable +{ + protected boolean initialized = false; + //private boolean disposed = false; + /** + * The property specifying a set of additional class loaders. + */ + private static final String CLASS_LOADER = "classloader"; + /** + * The property prefix specifying additional object factories. + */ + private static final String OBJECT_FACTORY = "object-factory"; + /** + * The name of the default factory. + */ + protected static final String DEFAULT_FACTORY = "default"; + /** + * Primitive classes for reflection of constructors. + */ + private static HashMap primitiveClasses; + { + primitiveClasses = new HashMap(8); + primitiveClasses.put(Boolean.TYPE.toString(), Boolean.TYPE); + primitiveClasses.put(Character.TYPE.toString(), Character.TYPE); + primitiveClasses.put(Byte.TYPE.toString(), Byte.TYPE); + primitiveClasses.put(Short.TYPE.toString(), Short.TYPE); + primitiveClasses.put(Integer.TYPE.toString(), Integer.TYPE); + primitiveClasses.put(Long.TYPE.toString(), Long.TYPE); + primitiveClasses.put(Float.TYPE.toString(), Float.TYPE); + primitiveClasses.put(Double.TYPE.toString(), Double.TYPE); + } + /** + * temporary storage of class names between configure and initialize + */ + private String[] loaderNames; + /** + * Additional class loaders. + */ + private ArrayList classLoaders = new ArrayList(); + /** + * Customized object factories. + */ + private HashMap objectFactories = new HashMap(); + /** + * Gets the class of a primitive type. + * + * @param type a primitive type. + * @return the corresponding class, or null. + */ + protected static Class getPrimitiveClass(String type) + { + return (Class) primitiveClasses.get(type); + } + /** + * Constructs a Factory Service. + */ + public DefaultFactoryService() + { + } + /** + * Gets an instance of a named class. + * + * @param className the name of the class. + * @return the instance. + * @throws FactoryException if instantiation fails. + */ + public Object getInstance(String className) throws FactoryException + { + if (className == null) + { + throw new FactoryException("Missing String className"); + } + Factory factory = getFactory(className); + if (factory == null) + { + Class clazz; + try + { + clazz = loadClass(className); + } + catch (ClassNotFoundException x) + { + throw new FactoryException("Instantiation failed for class " + className, x); + } + return getInstance(clazz); + } + else + { + return factory.getInstance(); + } + } + /** + * Gets an instance of a named class using a specified class loader. + * + * <p>Class loaders are supported only if the isLoaderSupported + * method returns true. Otherwise the loader parameter is ignored. + * + * @param className the name of the class. + * @param loader the class loader. + * @return the instance. + * @throws FactoryException if instantiation fails. + */ + public Object getInstance(String className, ClassLoader loader) throws FactoryException + { + Factory factory = getFactory(className); + if (factory == null) + { + if (loader != null) + { + Class clazz; + try + { + clazz = loadClass(className, loader); + } + catch (ClassNotFoundException x) + { + throw new FactoryException("Instantiation failed for class " + className, x); + } + return getInstance(clazz); + } + else + { + return getInstance(className); + } + } + else + { + return factory.getInstance(loader); + } + } + /** + * Gets an instance of a named class. + * 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 params an array containing the parameters of the constructor. + * @param signature an array containing the signature of the constructor. + * @return the instance. + * @throws FactoryException if instantiation fails. + */ + public Object getInstance(String className, Object[] params, String[] signature) throws FactoryException + { + Factory factory = getFactory(className); + if (factory == null) + { + Class clazz; + try + { + clazz = loadClass(className); + } + catch (ClassNotFoundException x) + { + throw new FactoryException("Instantiation failed for class " + className, x); + } + return getInstance(clazz, params, signature); + } + else + { + return factory.getInstance(params, signature); + } + } + /** + * Gets an instance of a named class using a specified class loader. + * Parameters for its constructor are given as an array of objects, + * primitive types must be wrapped with a corresponding class. + * + * <p>Class loaders are supported only if the isLoaderSupported + * method returns true. Otherwise the loader parameter is ignored. + * + * @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 FactoryException if instantiation fails. + */ + public Object getInstance(String className, ClassLoader loader, Object[] params, String[] signature) + throws FactoryException + { + Factory factory = getFactory(className); + if (factory == null) + { + if (loader != null) + { + Class clazz; + try + { + clazz = loadClass(className, loader); + } + catch (ClassNotFoundException x) + { + throw new FactoryException("Instantiation failed for class " + className, x); + } + return getInstance(clazz, params, signature); + } + else + { + return getInstance(className, params, signature); + } + } + else + { + return factory.getInstance(loader, params, signature); + } + } + /** + * 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 FactoryException if test fails. + */ + public boolean isLoaderSupported(String className) throws FactoryException + { + Factory factory = getFactory(className); + return factory != null ? factory.isLoaderSupported() : true; + } + /** + * Gets an instance of a specified class. + * + * @param clazz the class. + * @return the instance. + * @throws FactoryException if instantiation fails. + */ + public Object getInstance(Class clazz) throws FactoryException + { + try + { + return clazz.newInstance(); + } + catch (Exception x) + { + throw new FactoryException("Instantiation failed for " + clazz.getName(), x); + } + } + /** + * Gets an instance of a specified class. + * Parameters for its constructor are given as an array of objects, + * primitive types must be wrapped with a corresponding class. + * + * @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 FactoryException if instantiation fails. + */ + protected Object getInstance(Class clazz, Object params[], String signature[]) throws FactoryException + { + /* Try to construct. */ + try + { + Class[] sign = getSignature(clazz, params, signature); + return clazz.getConstructor(sign).newInstance(params); + } + catch (Exception x) + { + throw new FactoryException("Instantiation failed for " + clazz.getName(), x); + } + } + /** + * Gets the signature classes for parameters of a method of a class. + * + * @param clazz the class. + * @param params an array containing the parameters of the method. + * @param signature an array containing the signature of the method. + * @return an array of signature classes. Note that in some cases + * objects in the parameter array can be switched to the context + * of a different class loader. + * @throws ClassNotFoundException if any of the classes is not found. + */ + public Class[] getSignature(Class clazz, Object params[], String signature[]) throws ClassNotFoundException + { + if (signature != null) + { + /* We have parameters. */ + ClassLoader tempLoader; + ClassLoader loader = clazz.getClassLoader(); + Class[] sign = new Class[signature.length]; + for (int i = 0; i < signature.length; i++) + { + /* Check primitive types. */ + sign[i] = getPrimitiveClass(signature[i]); + if (sign[i] == null) + { + /* Not a primitive one, continue building. */ + if (loader != null) + { + /* Use the class loader of the target object. */ + sign[i] = loader.loadClass(signature[i]); + tempLoader = sign[i].getClassLoader(); + if ((params[i] != null) + && (tempLoader != null) + && !tempLoader.equals(params[i].getClass().getClassLoader())) + { + /* + * The class uses a different class loader, + * switch the parameter. + */ + params[i] = switchObjectContext(params[i], loader); + } + } + else + { + /* Use the default class loader. */ + sign[i] = loadClass(signature[i]); + } + } + } + return sign; + } + else + { + return null; + } + } + /** + * Switches an object into the context of a different class loader. + * + * @param object an object to switch. + * @param loader the loader of the new context. + */ + protected Object switchObjectContext(Object object, ClassLoader loader) + { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + try + { + ObjectOutputStream out = new ObjectOutputStream(bout); + out.writeObject(object); + out.flush(); + } + catch (Exception x) + { + return object; + } + try + { + ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); + ObjectInputStreamForContext in = new ObjectInputStreamForContext(bin, loader); + return in.readObject(); + } + catch (Exception x) + { + return object; + } + } + /** + * Loads the named class using the default class loader. + * + * @param className the name of the class to load. + * @return the loaded class. + * @throws ClassNotFoundException if the class was not found. + */ + protected Class loadClass(String className) throws ClassNotFoundException + { + ClassLoader loader = this.getClass().getClassLoader(); + try + { + return loader != null ? loader.loadClass(className) : Class.forName(className); + } + catch (ClassNotFoundException x) + { + /* Go through additional loaders. */ + for (Iterator i = classLoaders.iterator(); i.hasNext();) + { + try + { + return ((ClassLoader) i.next()).loadClass(className); + } + catch (ClassNotFoundException xx) + { + } + } + /* Give up. */ + throw x; + } + } + /** + * Loads the named class using a specified class loader. + * + * @param className the name of the class to load. + * @param loader the loader to use. + * @return the loaded class. + * @throws ClassNotFoundException if the class was not found. + */ + protected Class loadClass(String className, ClassLoader loader) throws ClassNotFoundException + { + return loader != null ? loader.loadClass(className) : loadClass(className); + } + /** + * Gets a customized factory for a named class. If no class-specific + * factory is specified but a default factory is, will use the default + * factory. + * + * @param className the name of the class to load. + * @return the factory, or null if not specified and no default. + * @throws FactoryException if instantiation of the factory fails. + */ + protected Factory getFactory(String className) throws FactoryException + { + HashMap factories = objectFactories; + Object factory = factories.get(className); + if (factory == null) + { + //No named factory for this; try the default, if one + //exists. + factory = factories.get(DEFAULT_FACTORY); + } + if (factory != null) + { + if (factory instanceof String) + { + /* Not yet instantiated... */ + try + { + factory = (Factory) getInstance((String) factory); + ((Factory) factory).init(className); + } + catch (FactoryException x) + { + throw x; + } + catch (ClassCastException x) + { + throw new FactoryException("Incorrect factory " + (String) factory + " for class " + className, x); + } + factories = (HashMap) factories.clone(); + factories.put(className, factory); + objectFactories = factories; + } + return (Factory) factory; + } + else + { + return null; + } + } + // ---------------- Avalon Lifecycle Methods --------------------- + /** + * Avalon component lifecycle method + */ + public void configure(Configuration conf) throws ConfigurationException + { + final Configuration[] loaders = conf.getChildren(CLASS_LOADER); + if (loaders != null) + { + loaderNames = new String[loaders.length]; + for (int i = 0; i < loaders.length; i++) + { + loaderNames[i] = loaders[i].getValue(); + } + } + final Configuration factories = conf.getChild(OBJECT_FACTORY, false); + if (factories != null) + { + Configuration[] nameVal = factories.getChildren(); + for (int i = 0; i < nameVal.length; i++) + { + String key = nameVal[i].getName(); + String factory = nameVal[i].getValue(); + // Store the factory to the table as a string and + // instantiate it by using the service when needed. + objectFactories.put(key, factory); + } + } + } + /** + * 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 + { + if (loaderNames != null) + { + for (int i = 0; i < loaderNames.length; i++) + { + try + { + classLoaders.add(loadClass(loaderNames[i]).newInstance()); + } + catch (Exception x) + { + throw new Exception( + "No such class loader '" + loaderNames[i] + "' for DefaultFactoryService", + x); + } + } + loaderNames = null; + } + } +} diff --git a/src/java/org/apache/fulcrum/factory/Factory.java b/src/java/org/apache/fulcrum/factory/Factory.java new file mode 100644 index 0000000..841d30c --- /dev/null +++ b/src/java/org/apache/fulcrum/factory/Factory.java @@ -0,0 +1,110 @@ +package org.apache.fulcrum.factory; + + +/* + * 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. + */ + + + +/** + * Factory is an interface for object factories. Object factories + * can be registered with the Factory Service to support customized + * functionality during instantiation of specific classes that + * the service itself cannot provide. Examples include + * instantiation of XML parsers and secure sockets requiring + * provider specific initializations before instantiation. + * + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @author <a href="mailto:[email protected]">Stephen McConnell</a> + * @version $Id$ + */ +public interface Factory +{ + /** + * Initializes the factory. This method is called by + * the Factory Service before the factory is used. + * + * @param className the name of the production class + * @throws FactoryException if initialization fails. + */ + public void init(String className) + throws FactoryException; + + /** + * Gets an instance of a class. + * + * @return the instance. + * @throws FactoryException if instantiation fails. + */ + public Object getInstance() + throws FactoryException; + + /** + * Gets an instance of a class using a specified class loader. + * + * <p>Class loaders are supported only if the isLoaderSupported + * method returns true. Otherwise the loader parameter is ignored. + * + * @param loader the class loader. + * @return the instance. + * @throws FactoryException if instantiation fails. + */ + public Object getInstance(ClassLoader loader) + throws FactoryException; + + /** + * Gets an instance of a named class. + * Parameters for its constructor are given as an array of objects, + * primitive types must be wrapped with a corresponding 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 FactoryException if instantiation fails. + */ + public Object getInstance(Object[] params, + String[] signature) + throws FactoryException; + + /** + * Gets an instance of a named class using a specified class loader. + * Parameters for its constructor are given as an array of objects, + * primitive types must be wrapped with a corresponding class. + * + * <p>Class loaders are supported only if the isLoaderSupported + * method returns true. Otherwise the loader parameter is ignored. + * + * @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 FactoryException if instantiation fails. + */ + public Object getInstance(ClassLoader loader, + Object[] params, + String[] signature) + throws FactoryException; + + /** + * Tests if this object factory supports specified class loaders. + * + * @return true if class loaders are supported, false otherwise. + */ + public boolean isLoaderSupported(); +} diff --git a/src/java/org/apache/fulcrum/factory/FactoryException.java b/src/java/org/apache/fulcrum/factory/FactoryException.java new file mode 100644 index 0000000..63c1ee0 --- /dev/null +++ b/src/java/org/apache/fulcrum/factory/FactoryException.java @@ -0,0 +1,43 @@ +package org.apache.fulcrum.factory; + +/* + * 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 FactoryService + * + * @author <a href="mailto:[email protected]">Eric Pugh</a> + * @version $Id$ + */ +public class FactoryException extends Exception +{ + /** + * Serial number + */ + private static final long serialVersionUID = 8954422192583295720L; + + public FactoryException(String msg) + { + super(msg); + } + public FactoryException(String msg, Exception ex) + { + super(msg, ex); + } +} diff --git a/src/java/org/apache/fulcrum/factory/FactoryService.java b/src/java/org/apache/fulcrum/factory/FactoryService.java new file mode 100644 index 0000000..e4e2f5f --- /dev/null +++ b/src/java/org/apache/fulcrum/factory/FactoryService.java @@ -0,0 +1,140 @@ +package org.apache.fulcrum.factory; + + +/* + * 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 Factory Service instantiates objects using either default + * class loaders or a specified one. Whether specified class + * loaders are supported for a class depends on implementation + * and can be tested with the isLoaderSupported method. + * + * @author <a href="mailto:[email protected]">Eric Pugh</a> + * @author <a href="mailto:[email protected]">Ilkka Priha</a> + * @version $Id$ + */ +public interface FactoryService +{ + /** + * The key under which this component is known by an avalon container. + */ + String ROLE = FactoryService.class.getName(); + + + /** + * Gets an instance of a class. + * + * @param clazz the name of the class. + * @return the instance. + * @throws ServiceException if instantiation fails. + */ + public Object getInstance(Class clazz) + throws FactoryException; + + /** + * Gets an instance of a named class. + * + * @param className the name of the class. + * @return the instance. + * @throws ServiceException if instantiation fails. + */ + public Object getInstance(String className) + throws FactoryException; + + /** + * Gets an instance of a named class using a specified class loader. + * + * <p>Class loaders are supported only if the isLoaderSupported + * method returns true. Otherwise the loader parameter is ignored. + * + * @param className the name of the class. + * @param loader the class loader. + * @return the instance. + * @throws ServiceException if instantiation fails. + */ + public Object getInstance(String className, + ClassLoader loader) + throws FactoryException; + + /** + * Gets an instance of a named class. + * 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 params an array containing the parameters of the constructor. + * @param signature an array containing the signature of the constructor. + * @return the instance. + * @throws ServiceException if instantiation fails. + */ + public Object getInstance(String className, + Object[] params, + String[] signature) + throws FactoryException; + + /** + * Gets an instance of a named class using a specified class loader. + * Parameters for its constructor are given as an array of objects, + * primitive types must be wrapped with a corresponding class. + * + * <p>Class loaders are supported only if the isLoaderSupported + * method returns true. Otherwise the loader parameter is ignored. + * + * @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 ServiceException if instantiation fails. + */ + public Object getInstance(String className, + ClassLoader loader, + Object[] params, + String[] signature) + throws FactoryException; + + /** + * 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 ServiceException if test fails. + */ + public boolean isLoaderSupported(String className) + throws FactoryException; + + /** + * Gets the signature classes for parameters of a method of a class. + * + * @param clazz the class. + * @param params an array containing the parameters of the method. + * @param signature an array containing the signature of the method. + * @return an array of signature classes. Note that in some cases + * objects in the parameter array can be switched to the context + * of a different class loader. + * @throws ClassNotFoundException if any of the classes is not found. + */ + Class[] getSignature(Class clazz, + Object params[], + String signature[]) + throws ClassNotFoundException; +} diff --git a/src/java/org/apache/fulcrum/factory/utils/ObjectInputStreamForContext.java b/src/java/org/apache/fulcrum/factory/utils/ObjectInputStreamForContext.java new file mode 100644 index 0000000..a0fb934 --- /dev/null +++ b/src/java/org/apache/fulcrum/factory/utils/ObjectInputStreamForContext.java @@ -0,0 +1,70 @@ +package org.apache.fulcrum.factory.utils; + + +/* + * 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/test/TestComponentConfig.xml b/src/test/TestComponentConfig.xml new file mode 100644 index 0000000..d027290 --- /dev/null +++ b/src/test/TestComponentConfig.xml @@ -0,0 +1,23 @@ +<?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> + + <factory/> +</componentConfig> diff --git a/src/test/TestRoleConfig.xml b/src/test/TestRoleConfig.xml new file mode 100644 index 0000000..8918105 --- /dev/null +++ b/src/test/TestRoleConfig.xml @@ -0,0 +1,28 @@ +<?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-list> diff --git a/src/test/org/apache/fulcrum/factory/FactoryServiceTest.java b/src/test/org/apache/fulcrum/factory/FactoryServiceTest.java new file mode 100644 index 0000000..eb97a08 --- /dev/null +++ b/src/test/org/apache/fulcrum/factory/FactoryServiceTest.java @@ -0,0 +1,131 @@ +package org.apache.fulcrum.factory; + +/* + * 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.util.ArrayList; +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 FactoryServiceTest extends BaseUnitTest +{ + private FactoryService factoryService = null; + /** + * Defines the testcase name for JUnit. + * + * @param name the testcase's name. + */ + public FactoryServiceTest(String name) + { + super(name); + } + + public void setUp() throws Exception + { + super.setUp(); + factoryService = (FactoryService) this.resolve( FactoryService.class.getName() ); + + } + /* + * Class to test for Object getInstance(String) + */ + public void testGetInstanceString() throws Exception + { + Object object = factoryService.getInstance("java.lang.StringBuffer"); + assertTrue(object instanceof StringBuffer); + } + /* + * Class to test for Object getInstance(String, ClassLoader) + */ + public void testGetInstanceStringClassLoader() throws Exception + { + Object object = factoryService.getInstance("java.lang.StringBuffer", StringBuffer.class.getClassLoader()); + assertTrue(object instanceof StringBuffer); + } + /* + * Class to test for Object getInstance(String, Object[], String[]) + */ + public void testGetInstanceStringObjectArrayStringArray() throws Exception + { + Object params[] = new Object[1]; + String sourceValue = "testing"; + params[0] = sourceValue; + String signature[] = new String[1]; + signature[0] = "java.lang.String"; + Object object = factoryService.getInstance("java.lang.StringBuffer", params, signature); + assertTrue(object instanceof StringBuffer); + assertEquals(sourceValue, object.toString()); + + } + /* + * Class to test for Object getInstance(String, ClassLoader, Object[], String[]) + */ + public void testGetInstanceStringClassLoaderObjectArrayStringArray() throws Exception + { + Object params[] = new Object[1]; + String sourceValu = "testing"; + params[0] = sourceValu; + String signature[] = new String[1]; + signature[0] = "java.lang.String"; + Object object = + factoryService.getInstance( + "java.lang.StringBuffer", + StringBuffer.class.getClassLoader(), + params, + signature); + assertTrue(object instanceof StringBuffer); + assertEquals(sourceValu, object.toString()); + + } + /** + * @todo Need to run a test where the loader is NOT supported. + * @throws Exception + */ + public void testIsLoaderSupported() throws Exception + { + assertTrue(factoryService.isLoaderSupported("java.lang.String")); + + } + public void testGetSignature() throws Exception + { + Object params[] = new Object[1]; + String sourceValu = "testing"; + params[0] = sourceValu; + String signature[] = new String[1]; + signature[0] = "java.lang.String"; + Class[] results = factoryService.getSignature(StringBuffer.class, params, signature); + assertEquals(1, results.length); + assertTrue(results[0].equals(String.class)); + + Integer sourceValueInteger = new Integer(10); + params[0] = sourceValueInteger; + signature[0] = "java.lang.Integer"; + results = factoryService.getSignature(ArrayList.class, params, signature); + assertEquals(1, results.length); + assertTrue("Result:" + results[0].getName(),results[0].equals(Integer.class)); + + } +} diff --git a/src/test/org/apache/fulcrum/factory/utils/ObjectInputStreamForContextTest.java b/src/test/org/apache/fulcrum/factory/utils/ObjectInputStreamForContextTest.java new file mode 100644 index 0000000..f00fa7e --- /dev/null +++ b/src/test/org/apache/fulcrum/factory/utils/ObjectInputStreamForContextTest.java @@ -0,0 +1,57 @@ +package org.apache.fulcrum.factory.utils; + +/* + * 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; +import junit.framework.TestCase; + +/** + * @author Eric Pugh + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ + +public class ObjectInputStreamForContextTest extends TestCase +{ + public static void main(String[] args) + { + junit.textui.TestRunner.run(ObjectInputStreamForContextTest.class); + } + /* + * Class to test for void ObjectInputStreamForContext(InputStream, ClassLoader) + */ + public void testObjectInputStreamForContextInputStreamClassLoader() throws Exception + { + Object object = new String("I am testing"); + Object object2 = null; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bout); + out.writeObject(object); + out.flush(); + ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); + ObjectInputStreamForContext in = new ObjectInputStreamForContext(bin, String.class.getClassLoader()); + object2 = in.readObject(); + assertEquals(object.toString(), object2.toString()); + assertEquals(object, object2); + } +} diff --git a/xdocs/changes.xml b/xdocs/changes.xml new file mode 100644 index 0000000..bcc5886 --- /dev/null +++ b/xdocs/changes.xml @@ -0,0 +1,42 @@ +<?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 Factory</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> + </release> + <release version="1.0.2" date=""> + <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..74e7d09 --- /dev/null +++ b/xdocs/index.xml @@ -0,0 +1,198 @@ +<?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>Factory Component</title> + <author email="[email protected]">Eric PUgh</author> + </properties> + + <body> + + <section name="Overview"> +<p> +The Factory Service instantiates objects from the given class name +using either the given class loader or an applicable one found from the class +loader repository. If neither one is specified, the default class loader +will be used. +</p> + +<p> +The service provides the following benefits compared to Class.forName(): +</p> +<ul> +<li>support for parameters in constructors,</li> +<li>internal class loader repository, which can be specified in resources,</li> +<li>optional class specific factories, which can be used for customized instantiation, and</li> +<li>integration with the Pool Service supporting recycling of instances created by the service.</li> +</ul> + </section> + +<section name="Configuration"> + + <p> + First, here is the role configuration. + </p> + +<source> +<![CDATA[ + <role + name="org.apache.fulcrum.factory.FactoryService" + shorthand="factory" + default-class="org.apache.fulcrum.factory.DefaultFactoryService"/> +]]> +</source> + + <p> + There is configuration values for classloaders, still need to add. + </p> +<source> + +<![CDATA[ + <factory/> +]]> +</source> + + </section> + + <section name="Usage"> + +<p> +In Turbine, the Factory Service is currently only used internally by the Pool Service. +Applications can also use the service instead of Class.forName() and for unifying +initialization, configuration and access to vendor specific object factories. +The following is a simplified example of a customized DOM parser factory: +</p> + +<source><![CDATA[ +package org.foo.xml; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.fulcrum.ServiceException; +import org.apache.fulcrum.factory.Factory; + +/** + * A factory for instantiating DOM parsers. + */ +public class DomBuilderFactory implements Factory +{ + /** + * The implementation of the factory. + */ + private DocumentBuilderFactory factory; + + /** + * Initializes the factory. + */ + public void init(String className) + throws ServiceException + { + factory = DocumentBuilderFactory.newInstance(); + } + + /** + * Gets a DocumentBuilder instance. + */ + public Object getInstance() + throws ServiceException + { + try + { + return factory.newDocumentBuilder(); + } + catch (ParserConfigurationException x) + { + throw new TurbineException(x); + } + } + + /** + * Gets a DocumentBuilder instance. + * The given loader is ignored. + */ + public Object getInstance(ClassLoader loader) + throws ServiceException + { + return getInstance(); + } + + /** + * Gets a DocumentBuilder instance. + * Constructor parameters are ignored. + */ + public Object getInstance(Object[] params, + String[] signature) + throws ServiceException + { + return getInstance(); + } + + /** + * Gets a DocumentBuilder instance. + * The given loader and constructor parameters are ignored. + */ + public Object getInstance(ClassLoader loader, + Object[] params, + String[] signature) + throws ServiceException + { + return getInstance(); + } + + /** + * Returns false as given class loaders are not supported. + */ + public boolean isLoaderSupported() + { + return false; + } +} +]]></source> + +<p> +The customized DOM parser factory must be specified in Turbine Resources before it can be used: +</p> + +<source><![CDATA[ +services.FactoryService.factory.javax.xml.parsers.DocumentBuilder=org.foo.xml.DomBuilderFactory +]]></source> + +<p> +A DOM parser can now be instantiated with the following code fragment: +</p> + +<source><![CDATA[ +// Access the service singleton. +FactoryService service = (FactoryService) + TurbineServices.getInstance().getService(FactoryService.SERVICE_NAME); + +// Create a new DOM parser. +DocumentBuilder parser = + service.getInstance("javax.xml.parsers.DocumentBuilder"); +]]></source> + + </section> + +</body> +</document> diff --git a/xdocs/navigation.xml b/xdocs/navigation.xml new file mode 100644 index 0000000..0fead1d --- /dev/null +++ b/xdocs/navigation.xml @@ -0,0 +1,38 @@ +<?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 Factory" + href="http://turbine.apache.org/fulcrum/fulcrum-factory/"> + + <body> + +&site-nav; + + <menu name="Overview"> + <item name="Main" href="/index.html"/> + <item name="Tasks" href="/tasks.html"/> + </menu> + </body> +</project> diff --git a/xdocs/tasks.xml b/xdocs/tasks.xml new file mode 100644 index 0000000..a8572e6 --- /dev/null +++ b/xdocs/tasks.xml @@ -0,0 +1,49 @@ +<?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>Tasks</title> + <author email="[email protected]">Eric Pugh</author> + </properties> + + <body> + <section name="Tasks"> + <p> + Lists of todos and ideas for future versions. + </p> + + <subsection name="1.0"> + <p> + <ul> + <li> + Need to test using the configuratio values. Currently it just uses the default Classloader. + </li> + <li> + Document using different classloaders. + </li> + </ul> + </p> + </subsection> + + </section> + + </body> +</document>
