001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.collections.functors;
018
019 import java.io.ByteArrayInputStream;
020 import java.io.ByteArrayOutputStream;
021 import java.io.IOException;
022 import java.io.ObjectInputStream;
023 import java.io.ObjectOutputStream;
024 import java.io.Serializable;
025 import java.lang.reflect.InvocationTargetException;
026 import java.lang.reflect.Method;
027
028 import org.apache.commons.collections.Factory;
029 import org.apache.commons.collections.FunctorException;
030
031 /**
032 * Factory implementation that creates a new instance each time based on a prototype.
033 *
034 * @since Commons Collections 3.0
035 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
036 *
037 * @author Stephen Colebourne
038 */
039 public class PrototypeFactory {
040
041 /**
042 * Factory method that performs validation.
043 * <p>
044 * Creates a Factory that will return a clone of the same prototype object
045 * each time the factory is used. The prototype will be cloned using one of these
046 * techniques (in order):
047 * <ul>
048 * <li>public clone method
049 * <li>public copy constructor
050 * <li>serialization clone
051 * <ul>
052 *
053 * @param prototype the object to clone each time in the factory
054 * @return the <code>prototype</code> factory
055 * @throws IllegalArgumentException if the prototype is null
056 * @throws IllegalArgumentException if the prototype cannot be cloned
057 */
058 public static Factory getInstance(Object prototype) {
059 if (prototype == null) {
060 return ConstantFactory.NULL_INSTANCE;
061 }
062 try {
063 Method method = prototype.getClass().getMethod("clone", (Class[]) null);
064 return new PrototypeCloneFactory(prototype, method);
065
066 } catch (NoSuchMethodException ex) {
067 try {
068 prototype.getClass().getConstructor(new Class[] { prototype.getClass()});
069 return new InstantiateFactory(
070 prototype.getClass(),
071 new Class[] { prototype.getClass()},
072 new Object[] { prototype });
073
074 } catch (NoSuchMethodException ex2) {
075 if (prototype instanceof Serializable) {
076 return new PrototypeSerializationFactory((Serializable) prototype);
077 }
078 }
079 }
080 throw new IllegalArgumentException("The prototype must be cloneable via a public clone method");
081 }
082
083 /**
084 * Constructor that performs no validation.
085 * Use <code>getInstance</code> if you want that.
086 */
087 private PrototypeFactory() {
088 super();
089 }
090
091 // PrototypeCloneFactory
092 //-----------------------------------------------------------------------
093 /**
094 * PrototypeCloneFactory creates objects by copying a prototype using the clone method.
095 */
096 static class PrototypeCloneFactory implements Factory, Serializable {
097
098 /** The serial version */
099 private static final long serialVersionUID = 5604271422565175555L;
100
101 /** The object to clone each time */
102 private final Object iPrototype;
103 /** The method used to clone */
104 private transient Method iCloneMethod;
105
106 /**
107 * Constructor to store prototype.
108 */
109 private PrototypeCloneFactory(Object prototype, Method method) {
110 super();
111 iPrototype = prototype;
112 iCloneMethod = method;
113 }
114
115 /**
116 * Find the Clone method for the class specified.
117 */
118 private void findCloneMethod() {
119 try {
120 iCloneMethod = iPrototype.getClass().getMethod("clone", (Class[]) null);
121
122 } catch (NoSuchMethodException ex) {
123 throw new IllegalArgumentException("PrototypeCloneFactory: The clone method must exist and be public ");
124 }
125 }
126
127 /**
128 * Creates an object by calling the clone method.
129 *
130 * @return the new object
131 */
132 public Object create() {
133 // needed for post-serialization
134 if (iCloneMethod == null) {
135 findCloneMethod();
136 }
137
138 try {
139 return iCloneMethod.invoke(iPrototype, (Object[])null);
140
141 } catch (IllegalAccessException ex) {
142 throw new FunctorException("PrototypeCloneFactory: Clone method must be public", ex);
143 } catch (InvocationTargetException ex) {
144 throw new FunctorException("PrototypeCloneFactory: Clone method threw an exception", ex);
145 }
146 }
147 }
148
149 // PrototypeSerializationFactory
150 //-----------------------------------------------------------------------
151 /**
152 * PrototypeSerializationFactory creates objects by cloning a prototype using serialization.
153 */
154 static class PrototypeSerializationFactory implements Factory, Serializable {
155
156 /** The serial version */
157 private static final long serialVersionUID = -8704966966139178833L;
158
159 /** The object to clone via serialization each time */
160 private final Serializable iPrototype;
161
162 /**
163 * Constructor to store prototype
164 */
165 private PrototypeSerializationFactory(Serializable prototype) {
166 super();
167 iPrototype = prototype;
168 }
169
170 /**
171 * Creates an object using serialization.
172 *
173 * @return the new object
174 */
175 public Object create() {
176 ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
177 ByteArrayInputStream bais = null;
178 try {
179 ObjectOutputStream out = new ObjectOutputStream(baos);
180 out.writeObject(iPrototype);
181
182 bais = new ByteArrayInputStream(baos.toByteArray());
183 ObjectInputStream in = new ObjectInputStream(bais);
184 return in.readObject();
185
186 } catch (ClassNotFoundException ex) {
187 throw new FunctorException(ex);
188 } catch (IOException ex) {
189 throw new FunctorException(ex);
190 } finally {
191 try {
192 if (bais != null) {
193 bais.close();
194 }
195 } catch (IOException ex) {
196 // ignore
197 }
198 try {
199 if (baos != null) {
200 baos.close();
201 }
202 } catch (IOException ex) {
203 // ignore
204 }
205 }
206 }
207 }
208
209 }