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 * https://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 */ 017package org.apache.commons.collections4.functors; 018 019import java.lang.reflect.Constructor; 020import java.lang.reflect.InvocationTargetException; 021import java.util.Objects; 022 023import org.apache.commons.collections4.Factory; 024import org.apache.commons.collections4.FunctorException; 025 026/** 027 * Factory implementation that creates a new object instance by reflection. 028 * <p> 029 * <strong>WARNING:</strong> from v4.1 onwards this class will <strong>not</strong> be serializable anymore 030 * in order to prevent potential remote code execution exploits. Please refer to 031 * <a href="https://issues.apache.org/jira/browse/COLLECTIONS-580">COLLECTIONS-580</a> 032 * for more details. 033 * </p> 034 * 035 * @param <T> The type of results supplied by this supplier. 036 * @since 3.0 037 */ 038public class InstantiateFactory<T> implements Factory<T> { 039 040 /** 041 * Factory method that performs validation. 042 * 043 * @param <T> the type the factory creates 044 * @param classToInstantiate The class to instantiate, not null 045 * @param paramTypes The constructor parameter types, cloned 046 * @param args The constructor arguments, cloned 047 * @return A new instantiate factory 048 * @throws NullPointerException if classToInstantiate is null 049 * @throws IllegalArgumentException if paramTypes does not match args 050 */ 051 public static <T> Factory<T> instantiateFactory(final Class<T> classToInstantiate, 052 final Class<?>[] paramTypes, 053 final Object[] args) { 054 Objects.requireNonNull(classToInstantiate, "classToInstantiate"); 055 if (paramTypes == null && args != null 056 || paramTypes != null && args == null 057 || paramTypes != null && args != null && paramTypes.length != args.length) { 058 throw new IllegalArgumentException("Parameter types must match the arguments"); 059 } 060 061 if (paramTypes == null || paramTypes.length == 0) { 062 return new InstantiateFactory<>(classToInstantiate); 063 } 064 return new InstantiateFactory<>(classToInstantiate, paramTypes, args); 065 } 066 067 /** The class to create */ 068 private final Class<T> iClassToInstantiate; 069 070 /** The constructor parameter types */ 071 private final Class<?>[] iParamTypes; 072 073 /** The constructor arguments */ 074 private final Object[] iArgs; 075 076 /** The constructor */ 077 private transient Constructor<T> iConstructor; 078 079 /** 080 * Constructor that performs no validation. 081 * Use {@code instantiateFactory} if you want that. 082 * 083 * @param classToInstantiate The class to instantiate 084 */ 085 public InstantiateFactory(final Class<T> classToInstantiate) { 086 iClassToInstantiate = classToInstantiate; 087 iParamTypes = null; 088 iArgs = null; 089 findConstructor(); 090 } 091 092 /** 093 * Constructor that performs no validation. 094 * Use {@code instantiateFactory} if you want that. 095 * 096 * @param classToInstantiate The class to instantiate 097 * @param paramTypes The constructor parameter types, cloned 098 * @param args The constructor arguments, cloned 099 */ 100 public InstantiateFactory(final Class<T> classToInstantiate, final Class<?>[] paramTypes, final Object[] args) { 101 iClassToInstantiate = classToInstantiate; 102 iParamTypes = paramTypes.clone(); 103 iArgs = args.clone(); 104 findConstructor(); 105 } 106 107 /** 108 * Creates an object using the stored constructor. 109 * 110 * @return The new object 111 */ 112 @Override 113 public T create() { 114 // needed for post-serialization 115 if (iConstructor == null) { 116 findConstructor(); 117 } 118 119 try { 120 return iConstructor.newInstance(iArgs); 121 } catch (final InstantiationException ex) { 122 throw new FunctorException("InstantiateFactory: InstantiationException", ex); 123 } catch (final IllegalAccessException ex) { 124 throw new FunctorException("InstantiateFactory: Constructor must be public", ex); 125 } catch (final InvocationTargetException ex) { 126 throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex); 127 } 128 } 129 130 /** 131 * Find the Constructor for the class specified. 132 */ 133 private void findConstructor() { 134 try { 135 iConstructor = iClassToInstantiate.getConstructor(iParamTypes); 136 } catch (final NoSuchMethodException ex) { 137 throw new IllegalArgumentException("InstantiateFactory: The constructor must exist and be public "); 138 } 139 } 140 141}