David Ostrovsky
2014-03-09 84f406bfd20ec2076cf7616e7f396ad480513bc4
Add plugins/extension infrastructure

Plugins are stored in `${baseFolder}/plugins` and are loaded
during startup by the PluginManager.

A plugin defines it's metadata in META-INF/MANIFEST.MF:

Plugin-Class: com.gitblit.plugins.cookbook.CookbookPlugin
Plugin-Dependencies: foo, bar
Plugin-Id: gitblit-plugin
Plugin-Provider: John Doe
Plugin-Version: 1.0

Plugins can define extension points that can be implemented
by other plugins and they can depend on other plugins:

Plugin-Dependencies: foo, bar

During the load phase, a directed acyclic graph is built and the loading
order of the dependency chain is reversed using a topological sort;
parent followed by children. The parent plugin classloader is the
combined classloader of all parent plugins.

Change-Id: I738821fa2bff02a5dbe339a944cc7e3c4dd8e299
2 files added
13 files modified
167 ■■■■■ changed files
.classpath 1 ●●●● patch | view | raw | blame | history
build.moxie 1 ●●●● patch | view | raw | blame | history
gitblit.iml 11 ●●●●● patch | view | raw | blame | history
src/main/distrib/data/gitblit.properties 8 ●●●●● patch | view | raw | blame | history
src/main/java/WEB-INF/web.xml 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/DaggerModule.java 13 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/FederationClient.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/GitBlit.java 7 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/dagger/DaggerFilter.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/GitblitManager.java 18 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/IGitblit.java 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/IPluginManager.java 39 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/PluginManager.java 56 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/servlet/GitblitContext.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/servlet/SyndicationFilter.java 1 ●●●● patch | view | raw | blame | history
.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" />
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
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>
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
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>
</web-app>
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(
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
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());
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() {
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);
    }
}
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.
src/main/java/com/gitblit/manager/IPluginManager.java
New file
@@ -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);
}
src/main/java/com/gitblit/manager/PluginManager.java
New file
@@ -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;
    }
}
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.");
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;