From 84f406bfd20ec2076cf7616e7f396ad480513bc4 Mon Sep 17 00:00:00 2001
From: David Ostrovsky <david@ostrovsky.org>
Date: Thu, 10 Apr 2014 18:58:09 -0400
Subject: [PATCH] Add plugins/extension infrastructure

---
 src/main/java/com/gitblit/manager/IPluginManager.java    |   39 +++++++++++++
 src/main/java/com/gitblit/dagger/DaggerFilter.java       |    2 
 src/main/java/com/gitblit/servlet/GitblitContext.java    |    2 
 .classpath                                               |    1 
 src/main/java/WEB-INF/web.xml                            |    3 
 src/main/distrib/data/gitblit.properties                 |    8 ++
 src/main/java/com/gitblit/GitBlit.java                   |    7 +
 src/main/java/com/gitblit/servlet/SyndicationFilter.java |    1 
 src/main/java/com/gitblit/DaggerModule.java              |   13 +++
 src/main/java/com/gitblit/manager/IGitblit.java          |    3 
 src/main/java/com/gitblit/manager/PluginManager.java     |   56 ++++++++++++++++++
 src/main/java/com/gitblit/FederationClient.java          |    2 
 src/main/java/com/gitblit/manager/GitblitManager.java    |   18 +++++
 build.moxie                                              |    1 
 gitblit.iml                                              |   11 +++
 15 files changed, 157 insertions(+), 10 deletions(-)

diff --git a/.classpath b/.classpath
index d3aec7e..252a7c9 100644
--- a/.classpath
+++ b/.classpath
@@ -76,6 +76,7 @@
 	<classpathentry kind="lib" path="ext/args4j-2.0.26.jar" sourcepath="ext/src/args4j-2.0.26.jar" />
 	<classpathentry kind="lib" path="ext/jedis-2.3.1.jar" sourcepath="ext/src/jedis-2.3.1.jar" />
 	<classpathentry kind="lib" path="ext/commons-pool2-2.0.jar" sourcepath="ext/src/commons-pool2-2.0.jar" />
+	<classpathentry kind="lib" path="ext/pf4j-0.6.jar" sourcepath="ext/src/pf4j-0.6.jar" />
 	<classpathentry kind="lib" path="ext/junit-4.11.jar" sourcepath="ext/src/junit-4.11.jar" />
 	<classpathentry kind="lib" path="ext/hamcrest-core-1.3.jar" sourcepath="ext/src/hamcrest-core-1.3.jar" />
 	<classpathentry kind="lib" path="ext/selenium-java-2.28.0.jar" sourcepath="ext/src/selenium-java-2.28.0.jar" />
diff --git a/build.moxie b/build.moxie
index 6015bec..eb2878a 100644
--- a/build.moxie
+++ b/build.moxie
@@ -174,6 +174,7 @@
 - compile 'args4j:args4j:2.0.26' :war :fedclient :authority
 - compile 'commons-codec:commons-codec:1.7' :war
 - compile 'redis.clients:jedis:2.3.1' :war
+- compile 'ro.fortsoft.pf4j:pf4j:0.6' :war
 - test 'junit'
 # Dependencies for Selenium web page testing
 - test 'org.seleniumhq.selenium:selenium-java:${selenium.version}' @jar
diff --git a/gitblit.iml b/gitblit.iml
index a48f12a..ed067f2 100644
--- a/gitblit.iml
+++ b/gitblit.iml
@@ -790,6 +790,17 @@
         </SOURCES>
       </library>
     </orderEntry>
+    <orderEntry type="module-library">
+      <library name="pf4j-0.6.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/pf4j-0.6.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/pf4j-0.6.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
     <orderEntry type="module-library" scope="TEST">
       <library name="junit-4.11.jar">
         <CLASSES>
diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties
index 64a52f5..762344b 100644
--- a/src/main/distrib/data/gitblit.properties
+++ b/src/main/distrib/data/gitblit.properties
@@ -1844,3 +1844,11 @@
 # SINCE 0.5.0
 # RESTART REQUIRED
 server.shutdownPort = 8081
+
+# Base folder for plugins.
+# This folder may contain Gitblit plugins
+#
+# SINCE 1.6.0
+# RESTART REQUIRED
+# BASEFOLDER
+plugins.folder = ${baseFolder}/plugins
diff --git a/src/main/java/WEB-INF/web.xml b/src/main/java/WEB-INF/web.xml
index 1451ec6..77456d4 100644
--- a/src/main/java/WEB-INF/web.xml
+++ b/src/main/java/WEB-INF/web.xml
@@ -199,7 +199,6 @@
 		<url-pattern>/robots.txt</url-pattern>
 	</servlet-mapping>
 
-	
 	<!-- Git Access Restriction Filter
 		 <url-pattern> MUST match: 
 			* GitServlet
@@ -322,4 +321,4 @@
         <url-pattern>/*</url-pattern>
     </filter-mapping>
     
-</web-app>
\ No newline at end of file
+</web-app>
diff --git a/src/main/java/com/gitblit/DaggerModule.java b/src/main/java/com/gitblit/DaggerModule.java
index b109f1d..1805c4e 100644
--- a/src/main/java/com/gitblit/DaggerModule.java
+++ b/src/main/java/com/gitblit/DaggerModule.java
@@ -19,9 +19,11 @@
 
 import com.gitblit.manager.AuthenticationManager;
 import com.gitblit.manager.FederationManager;
+import com.gitblit.manager.PluginManager;
 import com.gitblit.manager.IAuthenticationManager;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblit;
+import com.gitblit.manager.IPluginManager;
 import com.gitblit.manager.INotificationManager;
 import com.gitblit.manager.IProjectManager;
 import com.gitblit.manager.IRepositoryManager;
@@ -62,6 +64,7 @@
 			IRepositoryManager.class,
 			IProjectManager.class,
 			IFederationManager.class,
+			IPluginManager.class,
 
 			// the monolithic manager
 			IGitblit.class,
@@ -86,6 +89,10 @@
 
 	@Provides @Singleton IUserManager provideUserManager(IRuntimeManager runtimeManager) {
 		return new UserManager(runtimeManager);
+	}
+
+	@Provides @Singleton IPluginManager providePluginManager(IRuntimeManager runtimeManager) {
+		return new PluginManager(runtimeManager);
 	}
 
 	@Provides @Singleton IAuthenticationManager provideAuthenticationManager(
@@ -161,7 +168,8 @@
 			IPublicKeyManager publicKeyManager,
 			IRepositoryManager repositoryManager,
 			IProjectManager projectManager,
-			IFederationManager federationManager) {
+			IFederationManager federationManager,
+			IPluginManager pluginManager) {
 
 		return new GitBlit(
 				runtimeManager,
@@ -171,7 +179,8 @@
 				publicKeyManager,
 				repositoryManager,
 				projectManager,
-				federationManager);
+				federationManager,
+				pluginManager);
 	}
 
 	@Provides @Singleton GitBlitWebApp provideWebApplication(
diff --git a/src/main/java/com/gitblit/FederationClient.java b/src/main/java/com/gitblit/FederationClient.java
index d20025f..67a6865 100644
--- a/src/main/java/com/gitblit/FederationClient.java
+++ b/src/main/java/com/gitblit/FederationClient.java
@@ -97,7 +97,7 @@
 		UserManager users = new UserManager(runtime).start();
 		RepositoryManager repositories = new RepositoryManager(runtime, users).start();
 		FederationManager federation = new FederationManager(runtime, notifications, repositories).start();
-		IGitblit gitblit = new GitblitManager(runtime, notifications, users, null, null, repositories, null, federation);
+		IGitblit gitblit = new GitblitManager(runtime, notifications, users, null, null, repositories, null, federation, null);
 
 		FederationPullService puller = new FederationPullService(gitblit, federation.getFederationRegistrations()) {
 			@Override
diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java
index b223d03..147b2e1 100644
--- a/src/main/java/com/gitblit/GitBlit.java
+++ b/src/main/java/com/gitblit/GitBlit.java
@@ -28,6 +28,7 @@
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblit;
 import com.gitblit.manager.INotificationManager;
+import com.gitblit.manager.IPluginManager;
 import com.gitblit.manager.IProjectManager;
 import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.manager.IRuntimeManager;
@@ -71,7 +72,8 @@
 			IPublicKeyManager publicKeyManager,
 			IRepositoryManager repositoryManager,
 			IProjectManager projectManager,
-			IFederationManager federationManager) {
+			IFederationManager federationManager,
+			IPluginManager pluginManager) {
 
 		super(runtimeManager,
 				notificationManager,
@@ -80,7 +82,8 @@
 				publicKeyManager,
 				repositoryManager,
 				projectManager,
-				federationManager);
+				federationManager,
+				pluginManager);
 
 		this.injector = ObjectGraph.create(getModules());
 
diff --git a/src/main/java/com/gitblit/dagger/DaggerFilter.java b/src/main/java/com/gitblit/dagger/DaggerFilter.java
index 1c73d4b..68fe605 100644
--- a/src/main/java/com/gitblit/dagger/DaggerFilter.java
+++ b/src/main/java/com/gitblit/dagger/DaggerFilter.java
@@ -39,7 +39,7 @@
 		inject(objectGraph);
 	}
 
-	protected abstract void inject(ObjectGraph dagger);
+	protected abstract void inject(ObjectGraph dagger) throws ServletException;
 
 	@Override
 	public void destroy() {
diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java
index 8856715..34c101e 100644
--- a/src/main/java/com/gitblit/manager/GitblitManager.java
+++ b/src/main/java/com/gitblit/manager/GitblitManager.java
@@ -43,6 +43,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import ro.fortsoft.pf4j.PluginWrapper;
+
 import com.gitblit.Constants;
 import com.gitblit.Constants.AccessPermission;
 import com.gitblit.Constants.AccessRestrictionType;
@@ -116,6 +118,8 @@
 
 	protected final IFederationManager federationManager;
 
+	protected final IPluginManager pluginManager;
+
 	public GitblitManager(
 			IRuntimeManager runtimeManager,
 			INotificationManager notificationManager,
@@ -124,7 +128,8 @@
 			IPublicKeyManager publicKeyManager,
 			IRepositoryManager repositoryManager,
 			IProjectManager projectManager,
-			IFederationManager federationManager) {
+			IFederationManager federationManager,
+			IPluginManager pluginManager) {
 
 		this.settings = runtimeManager.getSettings();
 		this.runtimeManager = runtimeManager;
@@ -135,6 +140,7 @@
 		this.repositoryManager = repositoryManager;
 		this.projectManager = projectManager;
 		this.federationManager = federationManager;
+		this.pluginManager = pluginManager;
 	}
 
 	@Override
@@ -1171,4 +1177,14 @@
 	public boolean isIdle(Repository repository) {
 		return repositoryManager.isIdle(repository);
 	}
+
+	@Override
+	public <T> List<T> getExtensions(Class<T> clazz) {
+		return pluginManager.getExtensions(clazz);
+	}
+
+	@Override
+	public PluginWrapper whichPlugin(Class<?> clazz) {
+		return pluginManager.whichPlugin(clazz);
+	}
 }
diff --git a/src/main/java/com/gitblit/manager/IGitblit.java b/src/main/java/com/gitblit/manager/IGitblit.java
index f4221cf..7961a06 100644
--- a/src/main/java/com/gitblit/manager/IGitblit.java
+++ b/src/main/java/com/gitblit/manager/IGitblit.java
@@ -36,7 +36,8 @@
 									IAuthenticationManager,
 									IRepositoryManager,
 									IProjectManager,
-									IFederationManager {
+									IFederationManager,
+									IPluginManager {
 
 	/**
 	 * Returns a list of repository URLs and the user access permission.
diff --git a/src/main/java/com/gitblit/manager/IPluginManager.java b/src/main/java/com/gitblit/manager/IPluginManager.java
new file mode 100644
index 0000000..670e976
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/IPluginManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.manager;
+
+import java.util.List;
+
+import ro.fortsoft.pf4j.PluginWrapper;
+
+public interface IPluginManager extends IManager {
+
+	/**
+	 * Retrieves the extension for given class 'clazz'.
+	 *
+	 * @param clazz extension point class to retrieve extension for
+	 * @return list of extensions
+	 */
+	public <T> List<T> getExtensions(Class<T> clazz);
+
+	/**
+     * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'.
+     *
+     * @param clazz extension point class to retrieve extension for
+     * @return PluginWrapper that loaded the given class
+     */
+    public PluginWrapper whichPlugin(Class<?> clazz);
+}
diff --git a/src/main/java/com/gitblit/manager/PluginManager.java b/src/main/java/com/gitblit/manager/PluginManager.java
new file mode 100644
index 0000000..5eb00e9
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/PluginManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.manager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ro.fortsoft.pf4j.DefaultPluginManager;
+
+import com.gitblit.Keys;
+
+/**
+ * The plugin manager maintains the lifecycle of plugins. It is exposed as
+ * Dagger bean. The extension consumers supposed to retrieve plugin  manager
+ * from the Dagger DI and retrieve extensions provided by active plugins.
+ * 
+ * @author David Ostrovsky
+ * 
+ */
+public class PluginManager extends DefaultPluginManager implements
+		IPluginManager {
+
+	private final Logger logger = LoggerFactory.getLogger(getClass());
+
+	public PluginManager(IRuntimeManager runtimeManager) {
+		super(runtimeManager.getFileOrFolder(Keys.plugins.folder,
+				"${baseFolder}/plugins"));
+	}
+
+	@Override
+	public PluginManager start() {
+		logger.info("Plugin manager started");
+		loadPlugins();
+		startPlugins();
+		return this;
+	}
+
+	@Override
+	public PluginManager stop() {
+		stopPlugins();
+		return null;
+	}
+}
diff --git a/src/main/java/com/gitblit/servlet/GitblitContext.java b/src/main/java/com/gitblit/servlet/GitblitContext.java
index cf8bba0..a98fe19 100644
--- a/src/main/java/com/gitblit/servlet/GitblitContext.java
+++ b/src/main/java/com/gitblit/servlet/GitblitContext.java
@@ -41,6 +41,7 @@
 import com.gitblit.manager.IAuthenticationManager;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IGitblit;
+import com.gitblit.manager.IPluginManager;
 import com.gitblit.manager.IManager;
 import com.gitblit.manager.INotificationManager;
 import com.gitblit.manager.IProjectManager;
@@ -184,6 +185,7 @@
 		startManager(injector, IProjectManager.class);
 		startManager(injector, IFederationManager.class);
 		startManager(injector, IGitblit.class);
+		startManager(injector, IPluginManager.class);
 
 		logger.info("");
 		logger.info("All managers started.");
diff --git a/src/main/java/com/gitblit/servlet/SyndicationFilter.java b/src/main/java/com/gitblit/servlet/SyndicationFilter.java
index 67a845e..b399588 100644
--- a/src/main/java/com/gitblit/servlet/SyndicationFilter.java
+++ b/src/main/java/com/gitblit/servlet/SyndicationFilter.java
@@ -19,6 +19,7 @@
 import java.text.MessageFormat;
 
 import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;

--
Gitblit v1.9.1