001/* 002 * #%L 003 * Netarchivesuite - common 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023 024package dk.netarkivet.common.utils; 025 026import java.lang.reflect.Constructor; 027import java.lang.reflect.InvocationTargetException; 028import java.lang.reflect.Method; 029 030import dk.netarkivet.common.exceptions.ArgumentNotValid; 031import dk.netarkivet.common.exceptions.IOFailure; 032import dk.netarkivet.common.exceptions.PermissionDenied; 033 034/** 035 * Generic class for creating class instances from class names given in settings. 036 * 037 * @param <T> the object-type returned by this class. 038 */ 039public class SettingsFactory<T> { 040 041 /** 042 * Creates a new class of the class given in the settings field. 043 * <p> 044 * If the loaded class has a getInstance() method that matches the given arguments, that will be called to create 045 * the class, otherwise a matching constructor will be called, if it exists. This sequence allows for creating 046 * singletons. 047 * <p> 048 * Due to limitations of the Java Reflection API, the parameters of the getInstance method declared on the loaded 049 * class must match the given arguments exactly, without subclassing, interface implementation or unboxing. In 050 * particular, since any primitive types are automatically boxed when passed to this method, getInstance() methods 051 * with primitive type formal parameters will not be found. 052 * 053 * @param settingsField A field in the Settings class. 054 * @param args The arguments that will be passed to the getInstance method or the constructor. These will also be 055 * used to determine which getInstance method or constructor to find. 056 * @param <T> the object-type returned by this method. 057 * @return A new instance of type T created by calling getInstance() or by invoking a constructor. 058 * @throws ArgumentNotValid if settingsField is null or the invoked method or constructor threw an exception. 059 * @throws IOFailure if there are unrecoverable errors reflecting upon the class. 060 * @throws PermissionDenied if the class or methods cannot be accessed. 061 */ 062 @SuppressWarnings({"unchecked", "rawtypes"}) 063 public static <T> T getInstance(String settingsField, Object... args) { 064 ArgumentNotValid.checkNotNull(settingsField, "String settingsField"); 065 String className = Settings.get(settingsField); 066 try { 067 Class<T> aClass = (Class<T>) Class.forName(className); 068 Class[] classArgs = new Class[args.length]; 069 int i = 0; 070 for (Object o : args) { 071 classArgs[i] = o.getClass(); 072 ++i; 073 } 074 Method m = null; 075 try { 076 m = aClass.getMethod("getInstance", classArgs); 077 } catch (NoSuchMethodException e) { 078 // The exception is ignored, as we have an alternative 079 // approach in searching for constructors. 080 Constructor<T> c = null; 081 try { 082 c = aClass.getConstructor(classArgs); 083 } catch (NoSuchMethodException e1) { 084 throw new ArgumentNotValid("No suitable getInstance() or" + " constructor for class '" + className 085 + "'", e1); 086 } 087 try { 088 return c.newInstance(args); 089 } catch (InvocationTargetException e1) { 090 throw new ArgumentNotValid("Error creating singleton " + "of class '" + className + "': ", 091 e1.getCause()); 092 } 093 } 094 try { 095 return (T) m.invoke(null, args); 096 } catch (InvocationTargetException e) { 097 throw new ArgumentNotValid("Error creating singleton of class '" + className + "': ", e.getCause()); 098 } 099 } catch (IllegalAccessException e) { 100 throw new PermissionDenied("Cannot access class '" + className + "' defined by '" + settingsField + "'", e); 101 } catch (ClassNotFoundException e) { 102 throw new IOFailure("Error finding class '" + className + "' defined by '" + settingsField + "'", e); 103 } catch (InstantiationException e) { 104 throw new IOFailure("Error while instantiating class '" + className + "' defined by '" + settingsField 105 + "'", e); 106 } catch (ClassCastException e) { 107 throw new IOFailure("Set class '" + className + "' is of wrong type" + " defined by '" + settingsField 108 + "'", e); 109 } 110 } 111 112}