Add basic editable. Refactor plugin extension point mechanism.

This commit is contained in:
robin shine 2013-09-10 15:31:45 +08:00
parent 5920de5c9d
commit f1e37112c5
49 changed files with 829 additions and 106 deletions

View File

@ -16,12 +16,20 @@
<module>../commons.util</module>
<module>../commons.bootstrap</module>
<module>../commons.jetty</module>
<module>../commons.persistence</module>
<module>../commons.web</module>
<module>../commons.security</module>
<module>../commons.rest</module>
<module>../gitop</module>
<module>../gitop.core</module>
<module>../gitop.core</module>
<module>../commons.editable</module>
<module>../commons.git</module>
<module>../commons.hibernate</module>
<module>../commons.jersey</module>
<module>../commons.shiro</module>
<module>../commons.wicket</module>
<module>../gitop.product</module>
<module>../gitop.web</module>
</modules>
<repositories>
<repository>

61
commons.editable/pom.xml Normal file
View File

@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>commons.editable</artifactId>
<version>1.0.0</version>
<parent>
<groupId>com.pmease</groupId>
<artifactId>parent.general</artifactId>
<version>1.0.28</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>com.pmease</groupId>
<artifactId>plugin.maven</artifactId>
</plugin>
<!-- Uncomment below if you'd like to publish an archetype based on your plugin -->
<!--plugin>
<artifactId>maven-archetype-plugin</artifactId>
</plugin-->
<plugin>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.pmease</groupId>
<artifactId>commons.loader</artifactId>
<version>1.0.29</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>pmeaseRepo</id>
<name>PMEase Repository</name>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<url>http://artifact.pmease.com/</url>
</repository>
</repositories>
<properties>
<moduleClass>com.pmease.commons.editable.EditableModule</moduleClass>
</properties>
</project>

View File

@ -0,0 +1,30 @@
package com.pmease.commons.editable;
import java.lang.reflect.Method;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.pmease.commons.editable.typeconverter.TypeConverter;
@Singleton
public class DefaultTypeConverterRegistry implements TypeConverterRegistry {
private Set<TypeConverter> typeConverters;
@Inject
public DefaultTypeConverterRegistry(Set<TypeConverter> typeConverters) {
this.typeConverters = typeConverters;
}
@Override
public TypeConverter getTypeConverter(Method getter) {
for (TypeConverter each: typeConverters) {
if (each.accept(getter))
return each;
}
return null;
}
}

View File

@ -0,0 +1,26 @@
package com.pmease.commons.editable;
import com.pmease.commons.editable.typeconverter.TypeConverter;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
/**
* NOTE: Do not forget to rename moduleClass property defined in the pom if you've renamed this class.
*
*/
public class EditableModule extends AbstractPluginModule {
@Override
protected void configure() {
super.configure();
// put your guice bindings here
addExtensionsFromPackage(TypeConverter.class, TypeConverter.class);
}
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return EditablePlugin.class;
}
}

View File

@ -0,0 +1,13 @@
package com.pmease.commons.editable;
import java.util.Collection;
import com.pmease.commons.loader.AbstractPlugin;
public class EditablePlugin extends AbstractPlugin {
@Override
public Collection<?> getExtensions() {
return null;
}
}

View File

@ -0,0 +1,22 @@
package com.pmease.commons.editable;
import java.lang.reflect.Method;
import com.google.inject.ImplementedBy;
import com.pmease.commons.editable.typeconverter.TypeConverter;
@ImplementedBy(DefaultTypeConverterRegistry.class)
public interface TypeConverterRegistry {
/**
* Get type converter of the property given the property getter.
* <p>
* @param getter
* getter of the property
* @return
* type converter instance for the property, or <i>null</i> if no
* type converter matches the property getter
*/
TypeConverter getTypeConverter(Method getter);
}

View File

@ -0,0 +1,12 @@
package com.pmease.commons.editable.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ChoiceProvider {
String value();
}

View File

@ -0,0 +1,12 @@
package com.pmease.commons.editable.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Choices {
String[] value();
}

View File

@ -0,0 +1,17 @@
package com.pmease.commons.editable.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Editable {
String name() default "";
int order() default 0;
String description() default "";
}

View File

@ -0,0 +1,14 @@
package com.pmease.commons.editable.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ImplementationProvider {
Class<?> clazz() default Void.class;
String method();
}

View File

@ -0,0 +1,20 @@
/*
* Copyright PMEase (c) 2005-2008,
* Date: Feb 24, 2008
* Time: 4:29:05 PM
* All rights reserved.
*
* Revision: $Id: Multiline.java 1209 2008-07-28 00:16:18Z robin $
*/
package com.pmease.commons.editable.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Multiline {
int value() default 4;
}

View File

@ -0,0 +1,11 @@
package com.pmease.commons.editable.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Numeric {
}

View File

@ -0,0 +1,12 @@
package com.pmease.commons.editable.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {
boolean confirmative() default false;
}

View File

@ -0,0 +1,37 @@
package com.pmease.commons.editable.typeconverter;
import java.lang.reflect.Method;
public class BooleanConverter implements TypeConverter {
public boolean accept(Method getter) {
return getter.getReturnType() == boolean.class ||
getter.getReturnType() == Boolean.class;
}
public Object toObject(Class<?> type, String string) {
if (string == null)
return null;
if ("yes".equalsIgnoreCase(string) || "y".equalsIgnoreCase(string)
|| "true".equalsIgnoreCase(string) || "t".equalsIgnoreCase(string))
return Boolean.TRUE;
if ("no".equalsIgnoreCase(string) || "n".equalsIgnoreCase(string)
|| "false".equalsIgnoreCase(string) || "f".equalsIgnoreCase(string))
return Boolean.FALSE;
throw new ConversionException("Invalid boolean value: " + string);
}
public String toString(Object obj) {
if (obj == null)
return null;
if ((Boolean)obj)
return "yes";
else
return "no";
}
}

View File

@ -0,0 +1,42 @@
package com.pmease.commons.editable.typeconverter;
import java.lang.reflect.Method;
import com.pmease.commons.editable.annotation.Password;
import com.pmease.commons.util.StringUtils;
public class ConfirmativePasswordConverter implements TypeConverter {
public boolean accept(Method getter) {
return getter.getAnnotation(Password.class) != null
&& getter.getAnnotation(Password.class).confirmative();
}
public Object toObject(Class<?> type, String string) {
if (string != null) {
if (string.indexOf('\n') != -1) {
String[] parts = StringUtils.split(string, "\n");
if (parts.length == 0) {
return null;
} else if (parts.length != 2 || !parts[0].equals(parts[1])) {
throw new ConversionException("Password and it's " +
"confirmation should be identical.");
} else {
if (parts[0].length() == 0)
return null;
else
return parts[0];
}
} else {
return string;
}
} else {
return null;
}
}
public String toString(Object obj) {
return (String) obj;
}
}

View File

@ -0,0 +1,14 @@
package com.pmease.commons.editable.typeconverter;
import com.pmease.commons.util.ExceptionUtils;
@SuppressWarnings("serial")
public class ConversionException extends RuntimeException {
public ConversionException(String message) {
super(message);
}
public ConversionException(String message, Object...factors) {
super(ExceptionUtils.buildMessage(message, factors));
}
}

View File

@ -0,0 +1,31 @@
package com.pmease.commons.editable.typeconverter;
import java.lang.reflect.Method;
@SuppressWarnings({"rawtypes", "unchecked"})
public class EnumConverter implements TypeConverter {
public boolean accept(Method getter) {
return Enum.class.isAssignableFrom(getter.getReturnType());
}
public Object toObject(Class type, String string) {
if (string == null)
return null;
try {
return Enum.valueOf(type, string);
} catch (IllegalArgumentException e) {
throw new ConversionException("Invalid value of enum '" +
type.getName() + "'.");
}
}
public String toString(Object obj) {
if (obj == null)
return null;
else
return obj.toString();
}
}

View File

@ -0,0 +1,51 @@
package com.pmease.commons.editable.typeconverter;
import java.lang.reflect.Method;
import com.pmease.commons.editable.annotation.Numeric;
public class NumericConverter implements TypeConverter {
public boolean accept(Method getter) {
Class<?> type = getter.getReturnType();
return type == int.class || type == long.class || type == Integer.class ||
type == Long.class || type == float.class || type == Float.class ||
type == double.class || type == Double.class || getter.getAnnotation(Numeric.class) != null;
}
public Object toObject(Class<?> type, String string) {
if (string == null) {
if (type == int.class || type == long.class
|| type == float.class || type == double.class) {
throw new ConversionException("Should not be empty.");
} else {
return null;
}
}
try {
if (type == int.class || type == Integer.class) {
return Integer.parseInt(string);
} else if (type == long.class || type == Long.class) {
return Long.parseLong(string);
} else if (type == float.class || type == Float.class) {
return Float.parseFloat(string);
} else if (type == double.class || type == Double.class) {
return Double.parseDouble(string);
} else {
Long.parseLong(string);
return string;
}
} catch (NumberFormatException e) {
throw new ConversionException("Invalid numeric value.");
}
}
public String toString(Object obj) {
if (obj == null)
return null;
else
return obj.toString();
}
}

View File

@ -0,0 +1,23 @@
package com.pmease.commons.editable.typeconverter;
import java.lang.reflect.Method;
import com.pmease.commons.editable.annotation.Password;
public class PasswordConverter implements TypeConverter {
public boolean accept(Method getter) {
return getter.getReturnType() == String.class &&
getter.getAnnotation(Password.class) != null &&
!getter.getAnnotation(Password.class).confirmative();
}
public Object toObject(Class<?> type, String string) {
return string;
}
public String toString(Object obj) {
return (String) obj;
}
}

View File

@ -0,0 +1,27 @@
package com.pmease.commons.editable.typeconverter;
import java.lang.reflect.Method;
import com.pmease.commons.editable.annotation.Password;
public class StringConverter implements TypeConverter {
public boolean accept(Method getter) {
if (getter.getAnnotation(Password.class) != null)
return false;
else
return getter.getReturnType() == String.class;
}
public Object toObject(Class<?> type, String string) {
return string;
}
public String toString(Object obj) {
if (obj == null)
return null;
else
return obj.toString();
}
}

View File

@ -0,0 +1,12 @@
package com.pmease.commons.editable.typeconverter;
import java.lang.reflect.Method;
public interface TypeConverter {
boolean accept(Method getter);
String toString(Object obj);
Object toObject(Class<?> type, String string);
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor name="archetype.plugin">
<fileSets>
<fileSet filtered="true" packaged="true">
<directory>src/main/java</directory>
</fileSet>
<fileSet filtered="false" packaged="false">
<directory>src/main/resources</directory>
</fileSet>
<fileSet filtered="false" packaged="false">
<directory>src/test/java</directory>
</fileSet>
<fileSet filtered="false" packaged="false">
<directory>src/test/resources</directory>
</fileSet>
</fileSets>
</archetype-descriptor>

View File

@ -0,0 +1,60 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
#if (\${groupId} != "com.pmease")
<groupId>\${groupId}</groupId>
#end
<artifactId>\${artifactId}</artifactId>
<version>\${version}</version>
<parent>
<groupId>com.pmease</groupId>
<artifactId>parent.general</artifactId>
<version>1.0.28</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>com.pmease</groupId>
<artifactId>plugin.maven</artifactId>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>pmeaseRepo</id>
<name>PMEase Repository</name>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<url>http://artifact.pmease.com/</url>
</repository>
</repositories>
<properties>
<moduleClass>\${package}.PluginModule</moduleClass>
</properties>
</project>

View File

@ -0,0 +1,13 @@
package ${package};
import java.util.Collection;
import com.pmease.commons.loader.AbstractPlugin;
public class Plugin extends AbstractPlugin {
@Override
public Collection<?> getExtensions() {
return null;
}
}

View File

@ -0,0 +1,24 @@
package ${package};
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
/**
* NOTE: Do not forget to rename moduleClass property defined in the pom if you've renamed this class.
*
*/
public class PluginModule extends AbstractPluginModule {
@Override
protected void configure() {
super.configure();
// put your guice bindings here
}
@Override
protected Class<? extends AbstractPlugin> getPluginClass() {
return Plugin.class;
}
}

View File

@ -73,11 +73,6 @@
<artifactId>jtds</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.17.1-GA</version>
</dependency>
<dependency>
<groupId>com.pmease</groupId>
<artifactId>commons.jetty</artifactId>

View File

@ -3,6 +3,7 @@ package com.pmease.commons.hibernate;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Properties;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
@ -15,8 +16,6 @@ import org.hibernate.cfg.NamingStrategy;
import com.google.inject.Provider;
import com.pmease.commons.bootstrap.Bootstrap;
import com.pmease.commons.hibernate.extensionpoints.ModelContribution;
import com.pmease.commons.loader.PluginManager;
import com.pmease.commons.util.ClassUtils;
import com.pmease.commons.util.StringUtils;
@ -25,16 +24,16 @@ public class ConfigurationProvider implements Provider<Configuration> {
private Configuration configuration;
private final PluginManager pluginManager;
private final Set<ModelProvider> modelProviders;
private final NamingStrategy namingStrategy;
private final Properties hibernateProperties;
@Inject
public ConfigurationProvider(PluginManager pluginManager, NamingStrategy namingStrategy,
public ConfigurationProvider(Set<ModelProvider> modelProviders, NamingStrategy namingStrategy,
@Nullable @Named("hibernate") Properties hibernateProperties) {
this.pluginManager = pluginManager;
this.modelProviders = modelProviders;
this.namingStrategy = namingStrategy;
this.hibernateProperties = hibernateProperties;
}
@ -51,17 +50,15 @@ public class ConfigurationProvider implements Provider<Configuration> {
configuration = new Configuration();
configuration.setNamingStrategy(namingStrategy);
Collection<Class<AbstractEntity>> modelClasses =
Collection<Class<? extends AbstractEntity>> modelClasses =
ClassUtils.findSubClasses(AbstractEntity.class, AbstractEntity.class);
for (Class<AbstractEntity> model: modelClasses) {
for (Class<? extends AbstractEntity> model: modelClasses) {
if (!Modifier.isAbstract(model.getModifiers()))
configuration.addAnnotatedClass(model);
}
Collection<ModelContribution> contributions =
pluginManager.getExtensions(ModelContribution.class);
for (ModelContribution contribution: contributions) {
for (Class<? extends AbstractEntity> modelClass: contribution.getModelClasses())
for (ModelProvider provider: modelProviders) {
for (Class<? extends AbstractEntity> modelClass: provider.getModelClasses())
configuration.addAnnotatedClass(modelClass);
}

View File

@ -32,7 +32,6 @@ public class HibernateModule extends AbstractPluginModule {
bind(Configuration.class).toProvider(ConfigurationProvider.class);
bind(UnitOfWork.class).to(UnitOfWorkImpl.class);
bind(Session.class).toProvider(UnitOfWorkImpl.class);
bind(SessionProvider.class).to(UnitOfWorkImpl.class);
bind(GeneralDao.class).to(DefaultGeneralDao.class);

View File

@ -1,5 +1,7 @@
package com.pmease.commons.hibernate;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.EnumSet;
@ -10,8 +12,10 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.inject.TypeLiteral;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.util.ExceptionUtils;
public class HibernatePlugin extends AbstractPlugin {
@ -19,6 +23,21 @@ public class HibernatePlugin extends AbstractPlugin {
private final HibernateFilter hibernateFilter;
private static Field typeLiteralTypeField;
static {
try {
typeLiteralTypeField = TypeLiteral.class.getDeclaredField("type");
typeLiteralTypeField.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(typeLiteralTypeField, Modifier.PROTECTED);
} catch (Exception e) {
throw ExceptionUtils.unchecked(e);
}
}
@Inject
public HibernatePlugin(PersistService persistService, HibernateFilter hibernateFilter) {
this.persistService = persistService;

View File

@ -0,0 +1,7 @@
package com.pmease.commons.hibernate;
import java.util.Collection;
public interface ModelProvider {
Collection<Class<? extends AbstractEntity>> getModelClasses();
}

View File

@ -1,9 +0,0 @@
package com.pmease.commons.hibernate;
import javax.inject.Provider;
import org.hibernate.Session;
public interface SessionProvider extends Provider<Session> {
}

View File

@ -11,7 +11,7 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class UnitOfWorkImpl implements UnitOfWork, Provider<Session>, SessionProvider {
public class UnitOfWorkImpl implements UnitOfWork, Provider<Session> {
private final Provider<SessionFactory> sessionFactoryProvider;

View File

@ -1,9 +0,0 @@
package com.pmease.commons.hibernate.extensionpoints;
import java.util.Collection;
import com.pmease.commons.hibernate.AbstractEntity;
public interface ModelContribution {
Collection<Class<? extends AbstractEntity>> getModelClasses();
}

View File

@ -3,6 +3,7 @@ package com.pmease.commons.jetty;
import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
@ -24,7 +25,6 @@ import com.pmease.commons.bootstrap.BootstrapUtils;
import com.pmease.commons.jetty.extensionpoints.ServerConfigurator;
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.PluginManager;
public class JettyPlugin extends AbstractPlugin {
@ -32,11 +32,16 @@ public class JettyPlugin extends AbstractPlugin {
private ServletContextHandler context;
private final PluginManager pluginManager;
private final Set<ServerConfigurator> serverConfigurators;
private final Set<ServletContextConfigurator> servletContextConfigurators;
@Inject
public JettyPlugin(PluginManager pluginManager) {
this.pluginManager = pluginManager;
public JettyPlugin(
Set<ServerConfigurator> serverConfigurators,
Set<ServletContextConfigurator> servletContextConfigurators) {
this.serverConfigurators = serverConfigurators;
this.servletContextConfigurators = servletContextConfigurators;
}
@Override
@ -75,8 +80,6 @@ public class JettyPlugin extends AbstractPlugin {
context.addFilter(DisableTraceFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
Collection<ServletContextConfigurator> servletContextConfigurators =
pluginManager.getExtensions(ServletContextConfigurator.class);
for (ServletContextConfigurator configurator: servletContextConfigurators)
configurator.configure(context);
@ -89,9 +92,7 @@ public class JettyPlugin extends AbstractPlugin {
server.setHandler(context);
Collection<ServerConfigurator> servletContainerConfigurators =
pluginManager.getExtensions(ServerConfigurator.class);
for (ServerConfigurator configurator: servletContainerConfigurators)
for (ServerConfigurator configurator: serverConfigurators)
configurator.configure(server);
return server;

View File

@ -52,7 +52,11 @@
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
<version>${guiceVersion}</version>
</dependency>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
@ -72,7 +76,7 @@
</repository>
</repositories>
<properties>
<guiceVersion>3.0</guiceVersion>
<guiceVersion>4.0-beta</guiceVersion>
</properties>
<version>1.0.29</version>
</project>

View File

@ -11,6 +11,7 @@ import com.google.inject.multibindings.Multibinder;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
import com.pmease.commons.util.ClassUtils;
import com.pmease.commons.util.dependency.Dependency;
public abstract class AbstractPluginModule extends AbstractModule implements Dependency {
@ -31,8 +32,7 @@ public abstract class AbstractPluginModule extends AbstractModule implements Dep
protected void configure() {
final Class<? extends AbstractPlugin> pluginClass = getPluginClass();
if (pluginClass != null) {
Multibinder<AbstractPlugin> pluginBinder = Multibinder.newSetBinder(binder(), AbstractPlugin.class);
pluginBinder.addBinding().to(pluginClass).in(Singleton.class);
addExtension(AbstractPlugin.class, pluginClass);
bindListener(new AbstractMatcher<TypeLiteral<?>>() {
@ -101,4 +101,16 @@ public abstract class AbstractPluginModule extends AbstractModule implements Dep
return pluginDependencies;
}
protected <T> void addExtension(Class<T> extensionPoint, Class<? extends T> extensionClass) {
Multibinder<T> pluginBinder = Multibinder.newSetBinder(binder(), extensionPoint);
pluginBinder.addBinding().to(extensionClass).in(Singleton.class);
}
protected <T> void addExtensionsFromPackage(Class<T> extensionPoint, Class<?> packageLocator) {
for (Class<? extends T> subClass: ClassUtils.findSubClasses(extensionPoint, packageLocator)) {
if (ClassUtils.isConcrete(subClass))
addExtension(extensionPoint, subClass);
}
}
}

View File

@ -8,6 +8,16 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.bytecode.SignatureAttribute.ClassSignature;
import javassist.bytecode.SignatureAttribute.ClassType;
import javassist.bytecode.SignatureAttribute.TypeArgument;
import javassist.bytecode.SignatureAttribute.TypeParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -15,11 +25,14 @@ import org.slf4j.LoggerFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Modules;
import com.google.inject.util.Modules.OverriddenModuleBuilder;
import com.pmease.commons.bootstrap.Bootstrap;
import com.pmease.commons.bootstrap.Lifecycle;
import com.pmease.commons.util.ExceptionUtils;
import com.pmease.commons.util.FileUtils;
import com.pmease.commons.util.StringUtils;
import com.pmease.commons.util.dependency.DependencyHelper;
@ -30,6 +43,9 @@ public class AppLoader implements Lifecycle {
public static Injector injector;
@SuppressWarnings("rawtypes")
private static Map<String, TypeLiteral> typeLiterals = new HashMap<String, TypeLiteral>();
@Override
public void start() {
File tempDir = Bootstrap.getTempDir();
@ -117,4 +133,46 @@ public class AppLoader implements Lifecycle {
return injector.getInstance(type);
}
@SuppressWarnings({ "unchecked"})
public static <T> Set<T> getExtensions(Class<T> extensionPoint) {
synchronized (typeLiterals) {
TypeLiteral<Set<T>> typeLiteral = typeLiterals.get(extensionPoint.getName());
if (typeLiteral == null) {
try {
String packageName = extensionPoint.getPackage().getName();
String generatedTypeLiteralClassName =
"generated." + packageName + "." + extensionPoint.getSimpleName() + "TypeLiteral";
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(TypeLiteral.class));
CtClass ctGeneratedTypeLiteral = classPool.makeClass(generatedTypeLiteralClassName);
CtClass ctTypeLiteral = classPool.get(TypeLiteral.class.getName());
ctGeneratedTypeLiteral.setSuperclass(ctTypeLiteral);
CtConstructor constructor = new CtConstructor(new CtClass[0], ctGeneratedTypeLiteral);
constructor.setBody(null);
ctGeneratedTypeLiteral.addConstructor(constructor);
TypeArgument setTypeArgument = new TypeArgument(new ClassType(extensionPoint.getName()));
TypeArgument superClassTypeArgument = new TypeArgument(
new ClassType(Set.class.getName(), new TypeArgument[]{setTypeArgument}));
ClassType superClass = new ClassType(
TypeLiteral.class.getName(),
new TypeArgument[]{superClassTypeArgument});
ClassSignature signature = new ClassSignature(new TypeParameter[0], superClass, new ClassType[0]);
ctGeneratedTypeLiteral.setGenericSignature(signature.encode());
Class<?> typeLiteralClassOfExtensionPoint = ctGeneratedTypeLiteral.toClass();
typeLiteral = (TypeLiteral<Set<T>>) typeLiteralClassOfExtensionPoint.newInstance();
typeLiterals.put(extensionPoint.getName(), typeLiteral);
} catch (Exception e) {
throw ExceptionUtils.unchecked(e);
}
}
return injector.getInstance(Key.get(typeLiteral));
}
}
}

View File

@ -54,24 +54,6 @@ public class DefaultPluginManager implements PluginManager {
plugin.stop();
}
public <T> Collection<T> getExtensions(Class<T> extensionPoint) {
List<T> extensions = new ArrayList<T>();
for (AbstractPlugin plugin: pluginMap.values()) {
if (plugin.isEnabled()) {
Collection<?> pluginExtensions = plugin.getExtensions();
if (pluginExtensions != null) {
for (Object extension: pluginExtensions) {
if (extension != null && extensionPoint.isAssignableFrom(
extension.getClass())) {
extensions.add(extensionPoint.cast(extension));
}
}
}
}
}
return extensions;
}
@Override
public Collection<AbstractPlugin> getPlugins() {
return Collections.unmodifiableCollection(pluginMap.values());

View File

@ -6,8 +6,6 @@ import com.pmease.commons.bootstrap.Lifecycle;
public interface PluginManager extends Lifecycle {
<T> Collection<T> getExtensions(Class<T> extensionPoint);
Collection<AbstractPlugin> getPlugins();
<T extends AbstractPlugin> T getPlugin(Class<T> pluginClass);

View File

@ -1,12 +1,13 @@
package com.pmease.commons.shiro;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import com.pmease.commons.loader.PluginManager;
import com.pmease.commons.shiro.extensionpoint.FilterChainConfigurator;
@Singleton
@ -14,7 +15,7 @@ public class DefaultFilterChainResolver extends PathMatchingFilterChainResolver
@Inject
public DefaultFilterChainResolver(
PluginManager pluginManager,
Set<FilterChainConfigurator> filterChainConfigurators,
BasicAuthenticationFilter basicAuthenticationFilter) {
super();
@ -23,7 +24,7 @@ public class DefaultFilterChainResolver extends PathMatchingFilterChainResolver
filterChainManager.addFilter("authcBasic", basicAuthenticationFilter);
for (FilterChainConfigurator configurator: pluginManager.getExtensions(FilterChainConfigurator.class)) {
for (FilterChainConfigurator configurator: filterChainConfigurators) {
configurator.configure(filterChainManager);
}

View File

@ -4,6 +4,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
@ -27,21 +28,21 @@ public class ClassUtils extends org.apache.commons.lang3.ClassUtils {
*
* @param superClass
* super class (or interface) to match.
* @param packageClass
* @param packageLocatorClass
* find sub classes in the same package as this class. Package will not be searched recursively.
* @return collection of sub classes (not include the super class)
*/
@SuppressWarnings("unchecked")
public static <T> Collection<Class<T>> findSubClasses(Class<T> superClass, Class<?> packageClass) {
public static <T> Collection<Class<? extends T>> findSubClasses(Class<T> superClass, Class<?> packageLocatorClass) {
Preconditions.checkNotNull(superClass);
Preconditions.checkNotNull(packageClass);
Preconditions.checkNotNull(packageLocatorClass);
Collection<Class<T>> classes = new HashSet<Class<T>>();
Collection<Class<? extends T>> classes = new HashSet<Class<? extends T>>();
File location = new File(packageClass.getProtectionDomain()
File location = new File(packageLocatorClass.getProtectionDomain()
.getCodeSource().getLocation().getFile());
if (location.isFile()) {
String packagePath = packageClass.getPackage().getName().replace('.', '/') + "/";
String packagePath = packageLocatorClass.getPackage().getName().replace('.', '/') + "/";
JarFile jarFile;
try {
jarFile = new JarFile(location);
@ -68,14 +69,14 @@ public class ClassUtils extends org.apache.commons.lang3.ClassUtils {
}
}
} else {
String packagePath = packageClass.getPackage().getName().replace('.', File.separatorChar);
String packagePath = packageLocatorClass.getPackage().getName().replace('.', File.separatorChar);
File packageDir = new File(location, packagePath);
if (packageDir.exists()) {
for (File file: packageDir.listFiles()) {
if (file.getName().endsWith(".class")) {
Class<T> clazz;
try {
String className = packageClass.getPackage().getName() + "." +
String className = packageLocatorClass.getPackage().getName() + "." +
StringUtils.substringBeforeLast(file.getName(), ".");
clazz = (Class<T>) superClass.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
@ -181,5 +182,9 @@ public class ClassUtils extends org.apache.commons.lang3.ClassUtils {
return null;
}
}
public static boolean isConcrete(Class<?> clazz) {
return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers());
}
}

View File

@ -1,6 +1,7 @@
package com.pmease.commons.util;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import com.google.common.base.Preconditions;
@ -28,4 +29,16 @@ public class EasyMap {
return map;
}
public static Map<Object, Object> ofOrdered(Object...objects) {
Map<Object, Object> map = new LinkedHashMap<Object, Object>();
fill(map, objects);
return map;
}
public static Map<String, String> ofOrdered(String...strings) {
Map<String, String> map = new LinkedHashMap<String, String>();
fill(map, (Object[])strings);
return map;
}
}

View File

@ -1,6 +1,11 @@
package com.pmease.commons.util;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.pmease.commons.bootstrap.BootstrapUtils;
@ -31,4 +36,24 @@ public class ExceptionUtils extends org.apache.commons.lang3.exception.Exception
return BootstrapUtils.unchecked(e);
}
public static String buildMessage(String cause, Object...factors) {
return buildMessage(cause, EasyMap.ofOrdered(factors));
}
public static String buildMessage(String cause, Map<String, String> factors) {
String message = cause;
message += " (";
List<String> factorMessages = new ArrayList<String>();
for (Iterator<Map.Entry<String, String>> it = factors.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, String> entry = (Entry<String, String>) it.next();
factorMessages.add(entry.getKey().toString() + ":" + entry.getValue().toString());
}
message += StringUtils.join(factorMessages.iterator(), ", ");
message += ")";
return message;
}
}

View File

@ -57,7 +57,12 @@
<groupId>com.pmease</groupId>
<artifactId>commons.jetty</artifactId>
<version>1.0.29</version>
</dependency>
</dependency>
<dependency>
<groupId>com.pmease</groupId>
<artifactId>commons.editable</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<properties>
<moduleClass>com.pmease.commons.wicket.WicketModule</moduleClass>

View File

@ -0,0 +1,21 @@
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

@ -3,9 +3,7 @@ package com.pmease.gitop.core;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import javax.inject.Inject;
@ -14,13 +12,9 @@ import org.apache.commons.lang3.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.commons.hibernate.extensionpoints.ModelContribution;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AppLoader;
import com.pmease.commons.util.ClassUtils;
import com.pmease.gitop.core.manager.InitManager;
import com.pmease.gitop.core.model.ModelLocator;
import com.pmease.gitop.core.setting.ServerConfig;
public class Gitop extends AbstractPlugin {
@ -41,18 +35,7 @@ public class Gitop extends AbstractPlugin {
@Override
public Collection<?> getExtensions() {
return Arrays.asList(
new ModelContribution() {
@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;
}
}
);
return null;
}
@SuppressWarnings("serial")

View File

@ -2,6 +2,7 @@ package com.pmease.gitop.core;
import org.hibernate.cfg.NamingStrategy;
import com.pmease.commons.hibernate.ModelProvider;
import com.pmease.commons.hibernate.PrefixedNamingStrategy;
import com.pmease.commons.loader.AbstractPlugin;
import com.pmease.commons.loader.AbstractPluginModule;
@ -20,6 +21,7 @@ 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);

View File

@ -56,7 +56,7 @@
</repositories>
<properties>
<moduleClass>com.pmease.gitop.web.PluginModule</moduleClass>
<moduleClass>com.pmease.gitop.web.WebModule</moduleClass>
</properties>
</project>

View File

@ -312,6 +312,11 @@
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.18.0-GA</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>