Rework plugin architecture.

This commit is contained in:
robin shine 2013-09-10 18:27:03 +08:00
parent 5a2fe0dc63
commit 6e4ac36b6c
27 changed files with 239 additions and 222 deletions

View File

@ -1,7 +1,6 @@
package com.pmease.commons.editable;
import com.pmease.commons.editable.typeconverter.TypeConverter;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
/**
@ -18,9 +17,4 @@ public class EditableModule extends AbstractPluginModule {
addExtensionsFromPackage(TypeConverter.class, TypeConverter.class);
}
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return EditablePlugin.class;
}
}

View File

@ -1,7 +0,0 @@
package com.pmease.commons.editable;
import com.pmease.commons.loader.AbstractPlugin;
public class EditablePlugin extends AbstractPlugin {
}

View File

@ -1,6 +1,5 @@
package com.pmease.commons.git;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
/**
@ -16,9 +15,4 @@ public class GitModule extends AbstractPluginModule {
// put your guice bindings here
}
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return GitPlugin.class;
}
}

View File

@ -1,7 +0,0 @@
package com.pmease.commons.git;
import com.pmease.commons.loader.AbstractPlugin;
public class GitPlugin extends AbstractPlugin {
}

View File

@ -1,14 +0,0 @@
package com.pmease.commons.jersey;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import com.pmease.commons.shiro.extensionpoint.FilterChainConfigurator;
public class JerseyFilterChainConfigurator implements FilterChainConfigurator {
@Override
public void configure(FilterChainManager filterChainManager) {
filterChainManager.createChain("/" + JerseyPlugin.REST_PATH + "/**", "noSessionCreation, authcBasic");
}
}

View File

@ -1,6 +1,7 @@
package com.pmease.commons.jersey;
import com.pmease.commons.loader.AbstractPlugin;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import com.pmease.commons.loader.AbstractPluginModule;
import com.pmease.commons.shiro.extensionpoint.FilterChainConfigurator;
import com.pmease.commons.util.EasyMap;
@ -14,6 +15,8 @@ import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
*/
public class JerseyModule extends AbstractPluginModule {
public static final String REST_PATH = "rest";
@Override
protected void configure() {
super.configure();
@ -31,17 +34,19 @@ public class JerseyModule extends AbstractPluginModule {
}
protected String getRestPath() {
return "/" + JerseyPlugin.REST_PATH + "/*";
return "/" + REST_PATH + "/*";
}
});
addExtension(FilterChainConfigurator.class, JerseyFilterChainConfigurator.class);
}
addExtension(FilterChainConfigurator.class, new FilterChainConfigurator() {
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return JerseyPlugin.class;
@Override
public void configure(FilterChainManager filterChainManager) {
filterChainManager.createChain("/" + REST_PATH + "/**", "noSessionCreation, authcBasic");
}
});
}
}

View File

@ -1,9 +0,0 @@
package com.pmease.commons.jersey;
import com.pmease.commons.loader.AbstractPlugin;
public class JerseyPlugin extends AbstractPlugin {
public static final String REST_PATH = "rest";
}

View File

@ -4,10 +4,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.pmease.commons.bootstrap.Lifecycle;
import com.pmease.commons.util.dependency.Dependency;
public abstract class AbstractPlugin implements Dependency, Lifecycle {
public abstract class AbstractPlugin implements Plugin {
private String id;
@ -57,10 +54,6 @@ public abstract class AbstractPlugin implements Dependency, Lifecycle {
return description;
}
public boolean isEnabled() {
return true;
}
public final void setDescription(String description) {
this.description = description;
}
@ -73,29 +66,13 @@ public abstract class AbstractPlugin implements Dependency, Lifecycle {
public final void setDependencyIds(Set<String> dependencyIds) {
this.dependencyIds = new HashSet<String>(dependencyIds);
}
/**
* This function will be called before starting other plugins depending on this plugin.
*/
public void start() {
}
/**
* This function will be called after other plugins depending on this plugin have been started.
*/
@Override
public void postStart() {
}
/**
* This function will be called before stopping other plugins depending on this plugin.
*/
@Override
public void preStop() {
}
/**
* This function will be called after other plugins depending on this plugin have been stopped.
*/
public void stop() {
}
}

View File

@ -30,9 +30,9 @@ public abstract class AbstractPluginModule extends AbstractModule implements Dep
@Override
protected void configure() {
final Class<? extends AbstractPlugin> pluginClass = getPluginClass();
final Class<? extends Plugin> pluginClass = getPluginClass();
if (pluginClass != null) {
addExtension(AbstractPlugin.class, pluginClass);
addExtension(Plugin.class, pluginClass);
bindListener(new AbstractMatcher<TypeLiteral<?>>() {
@ -62,10 +62,62 @@ public abstract class AbstractPluginModule extends AbstractModule implements Dep
}
});
} else {
addExtension(Plugin.class, new Plugin() {
@Override
public String getId() {
return pluginId;
}
@Override
public String getName() {
return pluginName;
}
@Override
public String getVendor() {
return pluginVendor;
}
@Override
public String getVersion() {
return pluginVersion;
}
@Override
public String getDescription() {
return pluginDescription;
}
@Override
public Set<String> getDependencyIds() {
return pluginDependencies;
}
@Override
public void start() {
}
@Override
public void postStart() {
}
@Override
public void preStop() {
}
@Override
public void stop() {
}
});
}
}
protected abstract Class<? extends AbstractPlugin> getPluginClass();
protected Class<? extends AbstractPlugin> getPluginClass() {
return null;
}
public void setPluginId(String pluginId) {
this.pluginId = pluginId;
@ -106,6 +158,11 @@ public abstract class AbstractPluginModule extends AbstractModule implements Dep
pluginBinder.addBinding().to(extensionClass).in(Singleton.class);
}
protected <T> void addExtension(Class<T> extensionPoint, T extension) {
Multibinder<T> pluginBinder = Multibinder.newSetBinder(binder(), extensionPoint);
pluginBinder.addBinding().toInstance(extension);
}
protected <T> void addExtensionsFromPackage(Class<T> extensionPoint, Class<?> packageLocator) {
for (Class<? extends T> subClass: ClassUtils.findSubClasses(extensionPoint, packageLocator)) {
if (ClassUtils.isConcrete(subClass))

View File

@ -16,7 +16,7 @@ import com.pmease.commons.util.dependency.DependencyHelper;
public class DefaultPluginManager implements PluginManager {
// use linked hash map here to keep plugins in order
private final Map<String, AbstractPlugin> pluginMap = new LinkedHashMap<String, AbstractPlugin>();
private final Map<String, Plugin> pluginMap = new LinkedHashMap<String, Plugin>();
/**
* Construct plugin map in dependency order. Plugins without dependencies comes first in the
@ -25,11 +25,11 @@ public class DefaultPluginManager implements PluginManager {
* @param plugins
*/
@Inject
public DefaultPluginManager(final Set<AbstractPlugin> plugins) {
for (AbstractPlugin plugin: plugins)
public DefaultPluginManager(final Set<Plugin> plugins) {
for (Plugin plugin: plugins)
pluginMap.put(plugin.getId(), plugin);
for (AbstractPlugin plugin: DependencyHelper.sortDependencies(pluginMap)) {
for (Plugin plugin: DependencyHelper.sortDependencies(pluginMap)) {
// make sure the plugin map is in sorted order.
pluginMap.remove(plugin.getId());
pluginMap.put(plugin.getId(), plugin);
@ -37,39 +37,39 @@ public class DefaultPluginManager implements PluginManager {
}
public void start() {
for (AbstractPlugin plugin: pluginMap.values())
for (Plugin plugin: pluginMap.values())
plugin.start();
List<AbstractPlugin> reversed = new ArrayList<AbstractPlugin>(pluginMap.values());
List<Plugin> reversed = new ArrayList<Plugin>(pluginMap.values());
Collections.reverse(reversed);
for (AbstractPlugin plugin: reversed)
for (Plugin plugin: reversed)
plugin.postStart();
}
public void stop() {
for (AbstractPlugin plugin: pluginMap.values())
for (Plugin plugin: pluginMap.values())
plugin.preStop();
List<AbstractPlugin> reversed = new ArrayList<AbstractPlugin>(pluginMap.values());
List<Plugin> reversed = new ArrayList<Plugin>(pluginMap.values());
Collections.reverse(reversed);
for (AbstractPlugin plugin: reversed)
for (Plugin plugin: reversed)
plugin.stop();
}
@Override
public Collection<AbstractPlugin> getPlugins() {
public Collection<Plugin> getPlugins() {
return Collections.unmodifiableCollection(pluginMap.values());
}
@SuppressWarnings("unchecked")
@Override
public <T extends AbstractPlugin> T getPlugin(Class<T> pluginClass) {
for (AbstractPlugin plugin: pluginMap.values()) {
public <T extends Plugin> T getPlugin(Class<T> pluginClass) {
for (Plugin plugin: pluginMap.values()) {
if (plugin.getClass() == pluginClass)
return (T) plugin;
}
throw new RuntimeException("Unable to find plugin with class '" + pluginClass + "'.");
}
public AbstractPlugin getPlugin(String pluginId) {
public Plugin getPlugin(String pluginId) {
if (pluginMap.containsKey(pluginId))
return pluginMap.get(pluginId);
else

View File

@ -0,0 +1,42 @@
package com.pmease.commons.loader;
import java.util.Set;
import com.pmease.commons.bootstrap.Lifecycle;
import com.pmease.commons.util.dependency.Dependency;
public interface Plugin extends Dependency, Lifecycle {
String getId();
String getName();
String getVendor();
String getVersion();
String getDescription();
Set<String> getDependencyIds();
/**
* This function will be called before starting other plugins depending on this plugin.
*/
public void start();
/**
* This function will be called after other plugins depending on this plugin have been started.
*/
public void postStart();
/**
* This function will be called before stopping other plugins depending on this plugin.
*/
public void preStop();
/**
* This function will be called after other plugins depending on this plugin have been stopped.
*/
public void stop();
}

View File

@ -6,9 +6,9 @@ import com.pmease.commons.bootstrap.Lifecycle;
public interface PluginManager extends Lifecycle {
Collection<AbstractPlugin> getPlugins();
Collection<Plugin> getPlugins();
<T extends AbstractPlugin> T getPlugin(Class<T> pluginClass);
<T extends Plugin> T getPlugin(Class<T> pluginClass);
AbstractPlugin getPlugin(String pluginId);
Plugin getPlugin(String pluginId);
}

View File

@ -21,20 +21,44 @@ public class AbstractPluginTest {
@Before
public void before() {
plugin1 = spy(new AbstractPlugin() {
@Override
public void start() {
}
@Override
public void stop() {
}
});
plugin1.setId("plugin1");
plugin1.setDependencyIds(ImmutableSet.of("plugin2", "plugin3"));
plugin2 = spy(new AbstractPlugin() {
@Override
public void start() {
}
@Override
public void stop() {
}
});
plugin2.setId("plugin2");
plugin2.setDependencyIds(ImmutableSet.of("plugin3"));
plugin3 = spy(new AbstractPlugin() {
@Override
public void start() {
}
@Override
public void stop() {
}
});
plugin3.setId("plugin3");
Set<AbstractPlugin> plugins = new HashSet<AbstractPlugin>();
Set<Plugin> plugins = new HashSet<Plugin>();
plugins.add(plugin1);
plugins.add(plugin2);
plugins.add(plugin3);

View File

@ -1,16 +1,23 @@
package com.pmease.commons.shiro;
import java.util.EnumSet;
import javax.inject.Singleton;
import javax.servlet.DispatcherType;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.DefaultPasswordService;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.guice.aop.ShiroAopModule;
import org.apache.shiro.web.env.EnvironmentLoader;
import org.apache.shiro.web.env.EnvironmentLoaderListener;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.ShiroFilter;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
/**
@ -32,12 +39,20 @@ public class ShiroModule extends AbstractPluginModule {
install(new ShiroAopModule());
addExtension(ServletContextConfigurator.class, ShiroServletContextConfigurator.class);
}
addExtension(ServletContextConfigurator.class, new ServletContextConfigurator() {
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return ShiroPlugin.class;
@Override
public void configure(ServletContextHandler context) {
context.setInitParameter(
EnvironmentLoader.ENVIRONMENT_CLASS_PARAM,
DefaultWebEnvironment.class.getName());
context.addEventListener(new EnvironmentLoaderListener());
context.addFilter(new FilterHolder(new ShiroFilter()), "/*", EnumSet.allOf(DispatcherType.class));
}
});
}
}

View File

@ -1,7 +0,0 @@
package com.pmease.commons.shiro;
import com.pmease.commons.loader.AbstractPlugin;
public class ShiroPlugin extends AbstractPlugin {
}

View File

@ -1,28 +0,0 @@
package com.pmease.commons.shiro;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import org.apache.shiro.web.env.EnvironmentLoader;
import org.apache.shiro.web.env.EnvironmentLoaderListener;
import org.apache.shiro.web.servlet.ShiroFilter;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
public class ShiroServletContextConfigurator implements ServletContextConfigurator {
@Override
public void configure(ServletContextHandler context) {
context.setInitParameter(
EnvironmentLoader.ENVIRONMENT_CLASS_PARAM,
DefaultWebEnvironment.class.getName());
context.addEventListener(new EnvironmentLoaderListener());
context.addFilter(new FilterHolder(new ShiroFilter()), "/*", EnumSet.allOf(DispatcherType.class));
}
}

View File

@ -4,7 +4,6 @@ import org.apache.wicket.protocol.http.WicketFilter;
import org.apache.wicket.protocol.http.WicketServlet;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
public class WicketModule extends AbstractPluginModule {
@ -19,9 +18,4 @@ public class WicketModule extends AbstractPluginModule {
addExtension(ServletContextConfigurator.class, WicketServletContextConfigurator.class);
}
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return WicketPlugin.class;
}
}

View File

@ -1,7 +0,0 @@
package com.pmease.commons.wicket;
import com.pmease.commons.loader.AbstractPlugin;
public class WicketPlugin extends AbstractPlugin {
}

View File

@ -1,5 +1,7 @@
package com.pmease.commons.wicket;
import javax.inject.Inject;
import org.apache.wicket.protocol.http.WicketServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
@ -10,6 +12,7 @@ public class WicketServletContextConfigurator implements ServletContextConfigura
private final WicketServlet wicketServlet;
@Inject
public WicketServletContextConfigurator(WicketServlet wicketServlet) {
this.wicketServlet = wicketServlet;
}

View File

@ -1,21 +0,0 @@
package com.pmease.gitop.core;
import java.util.Collection;
import java.util.HashSet;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.commons.hibernate.ModelProvider;
import com.pmease.commons.util.ClassUtils;
import com.pmease.gitop.core.model.ModelLocator;
public class CoreModelProvider implements ModelProvider {
@Override
public Collection<Class<? extends AbstractEntity>> getModelClasses() {
Collection<Class<? extends AbstractEntity>> modelClasses =
new HashSet<Class<? extends AbstractEntity>>();
modelClasses.addAll(ClassUtils.findSubClasses(AbstractEntity.class, ModelLocator.class));
return modelClasses;
}
}

View File

@ -40,8 +40,6 @@ public class Gitop extends AbstractPlugin {
@SuppressWarnings("serial")
@Override
public void start() {
super.start();
manualConfigs = initManager.init();
if (!manualConfigs.isEmpty()) synchronized (manualConfigs) {
@ -120,5 +118,9 @@ public class Gitop extends AbstractPlugin {
public static <T> Set<T> getExtensions(Class<T> extensionPoint) {
return AppLoader.getExtensions(extensionPoint);
}
@Override
public void stop() {
}
}

View File

@ -1,12 +1,18 @@
package com.pmease.gitop.core;
import java.util.Collection;
import java.util.HashSet;
import org.hibernate.cfg.NamingStrategy;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.commons.hibernate.ModelProvider;
import com.pmease.commons.hibernate.PrefixedNamingStrategy;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
import com.pmease.commons.shiro.AbstractRealm;
import com.pmease.commons.util.ClassUtils;
import com.pmease.gitop.core.model.ModelLocator;
import com.pmease.gitop.core.permission.UserRealm;
/**
@ -21,10 +27,21 @@ public class GitopModule extends AbstractPluginModule {
bind(AbstractRealm.class).to(UserRealm.class);
addExtension(ModelProvider.class, CoreModelProvider.class);
bind(NamingStrategy.class).toInstance(new PrefixedNamingStrategy("G"));
bind(Gitop.class);
addExtension(ModelProvider.class, new ModelProvider() {
@Override
public Collection<Class<? extends AbstractEntity>> getModelClasses() {
Collection<Class<? extends AbstractEntity>> modelClasses =
new HashSet<Class<? extends AbstractEntity>>();
modelClasses.addAll(ClassUtils.findSubClasses(AbstractEntity.class, ModelLocator.class));
return modelClasses;
}
});
}
@Override

View File

@ -2,6 +2,8 @@ package com.pmease.gitop.product;
import java.io.File;
import javax.inject.Inject;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
@ -14,6 +16,7 @@ public class GitopServletContextConfigurator implements ServletContextConfigurat
private final ServerConfig serverConfig;
@Inject
public GitopServletContextConfigurator(ServerConfig serverConfig) {
this.serverConfig = serverConfig;
}

View File

@ -16,4 +16,12 @@ public class Product extends AbstractPlugin {
logger.info(NAME + " has been started successfully.");
}
@Override
public void start() {
}
@Override
public void stop() {
}
}

View File

@ -1,9 +1,16 @@
package com.pmease.gitop.web;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import com.pmease.commons.jetty.ClasspathAssetServlet;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
import com.pmease.commons.wicket.AbstractWicketConfig;
import com.pmease.gitop.web.asset.AssetLocator;
/**
* NOTE: Do not forget to rename moduleClass property defined in the pom if you've renamed this class.
@ -18,12 +25,19 @@ public class WebModule extends AbstractPluginModule {
// put your guice bindings here
bind(AbstractWicketConfig.class).to(WicketConfig.class);
addExtension(ServletContextConfigurator.class, WebServletContextConfigurator.class);
}
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return WebPlugin.class;
addExtension(ServletContextConfigurator.class, new ServletContextConfigurator() {
@Override
public void configure(ServletContextHandler context) {
ServletHolder servletHolder = new ServletHolder(new ClasspathAssetServlet(AssetLocator.class));
context.addServlet(servletHolder, "/asset/*");
context.addServlet(servletHolder, "/favicon.ico");
ErrorPageErrorHandler errorHandler = (ErrorPageErrorHandler) context.getErrorHandler();
errorHandler.addErrorPage(HttpServletResponse.SC_NOT_FOUND, "/asset/404.html");
}
});
}
}

View File

@ -1,7 +0,0 @@
package com.pmease.gitop.web;
import com.pmease.commons.loader.AbstractPlugin;
public class WebPlugin extends AbstractPlugin {
}

View File

@ -1,25 +0,0 @@
package com.pmease.gitop.web;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import com.pmease.commons.jetty.ClasspathAssetServlet;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.gitop.web.asset.AssetLocator;
public class WebServletContextConfigurator implements ServletContextConfigurator {
@Override
public void configure(ServletContextHandler context) {
ServletHolder servletHolder = new ServletHolder(new ClasspathAssetServlet(AssetLocator.class));
context.addServlet(servletHolder, "/asset/*");
context.addServlet(servletHolder, "/favicon.ico");
ErrorPageErrorHandler errorHandler = (ErrorPageErrorHandler) context.getErrorHandler();
errorHandler.addErrorPage(HttpServletResponse.SC_NOT_FOUND, "/asset/404.html");
}
}