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 }