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 */ 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 /** The class to create */ 067 private final Class<T> iClassToInstantiate; 068 /** The constructor parameter types */ 069 private final Class<?>[] iParamTypes; 070 /** The constructor arguments */ 071 private final Object[] iArgs; 072 073 /** The constructor */ 074 private transient Constructor<T> iConstructor; 075 076 /** 077 * Constructor that performs no validation. 078 * Use {@code instantiateFactory} if you want that. 079 * 080 * @param classToInstantiate the class to instantiate 081 */ 082 public InstantiateFactory(final Class<T> classToInstantiate) { 083 iClassToInstantiate = classToInstantiate; 084 iParamTypes = null; 085 iArgs = null; 086 findConstructor(); 087 } 088 089 /** 090 * Constructor that performs no validation. 091 * Use {@code instantiateFactory} if you want that. 092 * 093 * @param classToInstantiate the class to instantiate 094 * @param paramTypes the constructor parameter types, cloned 095 * @param args the constructor arguments, cloned 096 */ 097 public InstantiateFactory(final Class<T> classToInstantiate, final Class<?>[] paramTypes, final Object[] args) { 098 iClassToInstantiate = classToInstantiate; 099 iParamTypes = paramTypes.clone(); 100 iArgs = args.clone(); 101 findConstructor(); 102 } 103 104 /** 105 * Creates an object using the stored constructor. 106 * 107 * @return the new object 108 */ 109 @Override 110 public T create() { 111 // needed for post-serialization 112 if (iConstructor == null) { 113 findConstructor(); 114 } 115 116 try { 117 return iConstructor.newInstance(iArgs); 118 } catch (final InstantiationException ex) { 119 throw new FunctorException("InstantiateFactory: InstantiationException", ex); 120 } catch (final IllegalAccessException ex) { 121 throw new FunctorException("InstantiateFactory: Constructor must be public", ex); 122 } catch (final InvocationTargetException ex) { 123 throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex); 124 } 125 } 126 127 /** 128 * Find the Constructor for the class specified. 129 */ 130 private void findConstructor() { 131 try { 132 iConstructor = iClassToInstantiate.getConstructor(iParamTypes); 133 } catch (final NoSuchMethodException ex) { 134 throw new IllegalArgumentException("InstantiateFactory: The constructor must exist and be public "); 135 } 136 } 137 138}