Merge pull request #1171 from pingunaut/usermanager-file-instantiation

Update UserManager to support construction of IUserServices with IRuntimeManager as a constructor parameter
diff --git a/src/main/java/com/gitblit/IUserService.java b/src/main/java/com/gitblit/IUserService.java
index 6f3c542..468f968 100644
--- a/src/main/java/com/gitblit/IUserService.java
+++ b/src/main/java/com/gitblit/IUserService.java
@@ -26,6 +26,9 @@
  * Implementations of IUserService control all aspects of UserModel objects and

  * user authentication.

  *

+ * Plugins implementing this interface (which are instantiated during {@link com.gitblit.manager.UserManager#start()}) can provide

+ * a default constructor or might also use {@link IRuntimeManager} as a constructor argument which will be passed automatically then. 

+ *

  * @author James Moger

  *

  */

diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java
index e88ac93..d661c9b 100644
--- a/src/main/java/com/gitblit/manager/UserManager.java
+++ b/src/main/java/com/gitblit/manager/UserManager.java
@@ -17,6 +17,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -119,8 +121,10 @@
 					// typical file path configuration
 					File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf");
 					service = createUserService(realmFile);
-				} catch (InstantiationException | IllegalAccessException  e) {
-					logger.error("failed to instantiate user service {}: {}", realm, e.getMessage());
+				} catch (InstantiationException | IllegalAccessException e) {
+					logger.error("failed to instantiate user service {}: {}. Trying once again with IRuntimeManager constructor", realm, e.getMessage());
+					//try once again with IRuntimeManager constructor. This adds support for subclasses of ConfigUserService and other custom IUserServices
+					service = createIRuntimeManagerAwareUserService(realm);
 				}
 			}
 			setUserService(service);
@@ -128,6 +132,22 @@
 		return this;
 	}
 
+	/**
+	 * Tries to create an {@link IUserService} with {@link #runtimeManager} as a constructor parameter
+	 *
+	 * @param realm the class name of the {@link IUserService} to be instantiated
+	 * @return the {@link IUserService} or {@code null} if instantiation fails
+	 */
+	private IUserService createIRuntimeManagerAwareUserService(String realm) {
+		try {
+		    Constructor<?> constructor = Class.forName(realm).getConstructor(IRuntimeManager.class);
+		    return (IUserService) constructor.newInstance(runtimeManager);
+		} catch (NoSuchMethodException | SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+		    logger.error("failed to instantiate user service {}: {}", realm, e.getMessage());
+			return null;
+		}
+	}
+
 	protected IUserService createUserService(File realmFile) {
 		IUserService service = null;
 		if (realmFile.getName().toLowerCase().endsWith(".conf")) {